home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 43
/
Aminet 43 (2001)(GTI - Schatztruhe)[!][Jun 2001].iso
/
Aminet
/
game
/
misc
/
WormWars.lha
/
WormWars
/
Source
/
engine.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-04-21
|
184KB
|
4,968 lines
/* $Filename: WormWars/Source/engine.c
* $VER: WormWars 6.6
*
* © Copyright 2001 James R. Jacobs. Freely distributable.
*/
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE */
#include <assert.h>
#define ASSERT
#include "stdafx.h"
#include "diff.h"
#include "same.h"
#include "engine.h"
/* PRIVATE STRUCTURES -------------------------------------------------- */
struct
{ SBYTE x, y, deltax, deltay;
ABOOL alive, moved, teleported, visible, reflected;
} bullet[9];
struct
{ UWORD freq;
ULONG score;
} object[LASTOBJECT + 1] =
{ {1280, 60}, // AFFIXER
{ 60, 20}, // AMMO
{ 110, 20}, // ARMOUR
{ 70, 50}, // BIAS
{ 150, 30}, // BOMB
{ 80, 10}, // BONUS
{1020, 60}, // CLOCK
{ 380, 50}, // CONVERTER
{ 160, 80}, // CUTTER
{ 250, 90}, // CYCLONE
{ 500, 30}, // FREEDOM
{ 320, 50}, // GROWER
{1900, 90}, // HEALER
{1360, 60}, // ICE
{ 140, 60}, // LIFE
{ 160, 80}, // LIGHTNING
{ 970, 80}, // MAGNET
{ 240, 40}, // MISSILE
{ 640, 50}, // MULTIPLIER
{ 400, 10}, // NITRO
{ 240, 30}, // POWER
{ 480, 50}, // PROTECTOR
{ 210, 40}, // PULSE
{ 300, 50}, // PUSHER
{ 400, 40}, // REMNANTS
{ 500, 30}, // SIDESHOT
{ 600, 40}, // SLAYER
{ 980, 40}, // SLOWER
{ 730, 70}, // SWITCHER
{ 320, 20}, // TONGUE
{1400, 120}, // TREASURE
{3800, 140} // UMBRELLA
};
/* -200 common
220-400 uncommon
420-980 rare
1000+ very rare */
struct
{ SBYTE x, y, deltax, deltay, relx, rely;
ABOOL alive, last, visible;
} protector[4][PROTECTORS + 1];
struct
{ SBYTE deltax, deltay;
} thewormqueue[4][WORMQUEUELIMIT + 1];
struct
{ SBYTE deltax, deltay;
} thedogqueue[CREATURES + 1][DOGQUEUELIMIT + 1];
struct
{ ABOOL alive;
SBYTE x, y, player;
UBYTE object;
} magnet[MAGNETS + 1];
SBYTE eachworm[4][2][9] =
{ { { GREENHEADUP, GREENHEADUP, GREENHEADUP,
GREENHEADLEFT, ANYTHING, GREENHEADRIGHT,
GREENHEADDOWN, GREENHEADDOWN, GREENHEADDOWN
},
{ GREENMODEUP, GREENMODEUP, GREENMODEUP,
GREENMODELEFT, ANYTHING, GREENMODERIGHT,
GREENMODEDOWN, GREENMODEDOWN, GREENMODEDOWN
} },
{ { REDHEADUP, REDHEADUP, REDHEADUP,
REDHEADLEFT, ANYTHING, REDHEADRIGHT,
REDHEADDOWN, REDHEADDOWN, REDHEADDOWN
},
{ REDMODEUP, REDMODEUP, REDMODEUP,
REDMODELEFT, ANYTHING, REDMODERIGHT,
REDMODEDOWN, REDMODEDOWN, REDMODEDOWN
} },
{ { BLUEHEADUP, BLUEHEADUP, BLUEHEADUP,
BLUEHEADLEFT, ANYTHING, BLUEHEADRIGHT,
BLUEHEADDOWN, BLUEHEADDOWN, BLUEHEADDOWN
},
{ BLUEMODEUP, BLUEMODEUP, BLUEMODEUP,
BLUEMODELEFT, ANYTHING, BLUEMODERIGHT,
BLUEMODEDOWN, BLUEMODEDOWN, BLUEMODEDOWN
} },
{ { YELLOWHEADUP, YELLOWHEADUP, YELLOWHEADUP,
YELLOWHEADLEFT,ANYTHING, YELLOWHEADRIGHT,
YELLOWHEADDOWN,YELLOWHEADDOWN,YELLOWHEADDOWN
},
{ YELLOWMODEUP, YELLOWMODEUP, YELLOWMODEUP,
YELLOWMODELEFT,ANYTHING, YELLOWMODERIGHT,
YELLOWMODEDOWN,YELLOWMODEDOWN,YELLOWMODEDOWN
} } };
/* Rules for variable types:
SBYTE is used for field coordinates and queue indexes
UBYTE is used for field contents
SWORD is used for frequencies
ULONG is used for scores */
struct
{ ABOOL alive, explode, visible;
SBYTE x, y, deltax, deltay, pos, time, dir;
UBYTE mode, dormant, multi, speed, last, oldlast, species,
type; // type is relevant worm 0-3 (for drips, missiles and dogs)
SWORD armour, tongue, freq;
ULONG score;
} creature[CREATURES + 1];
// MODULE VARIABLES (used only within engine.c) ---------------------------
MODULE ABOOL banging = FALSE, letters[4][LETTERS + 1], trainer;
MODULE SBYTE freq, ice, lettertype, letterx, lettery, leveltype,
treasurer;
MODULE UBYTE infector[FIELDX + 1][FIELDY + 1];
MODULE ULONG octopusspeed;
// GLOBAL VARIABLES (owned by engine.c, imported by system.c) -------------
ABOOL clearthem = FALSE,
modified = FALSE,
randomflag = FALSE,
randomarray[MAXLEVELS + 1];
UBYTE board[MAXLEVELS + 1][FIELDX + 1][FIELDY + 1],
field[FIELDX + 1][FIELDY + 1];
SBYTE a = GAMEOVER,
players,
level = 1, levels, reallevel, sourcelevel,
startx[MAXLEVELS + 1], starty[MAXLEVELS + 1];
SWORD secondsleft, secondsperlevel;
TEXT pathname[81],
date[DATELENGTH + 1],
times[TIMELENGTH + 1];
ULONG delay, r;
AGLOBAL struct HiScoreStruct hiscore[HISCORES + 1];
AGLOBAL struct WormStruct worm[4];
AGLOBAL struct TeleportStruct teleport[MAXLEVELS + 1][4];
/* FUNCTIONS --------------------------------------------------------------
NAME align -- right-justify a string within another string
SYNOPSIS align(STRPTR, SBYTE, TEXT);
FUNCTION Moves all text in a string to the right, padding with
spaces. Does not itself add a null terminator.
INPUTS string - pointer to the string of text
size - size in characters of the containing string
filler - what to pad the left of the string with
NOTE Null terminators are written over by this function, but that
does not matter, because calling functions use Text() with an
explicit length. This function only works with monospaced
fonts.
MODULE engine.c */
void align(STRPTR string, SBYTE size, TEXT filler)
{ SBYTE i, shift, length;
length = strlen((const char*) string);
shift = size - length;
for (i = 1; i <= length; i++)
*(string + size - i) = *(string + size - i - shift);
for (i = 0; i <= shift - 1; i++)
*(string + i) = filler;
}
MODULE ABOOL blocked(SBYTE which, SBYTE deltax, SBYTE deltay)
{ UBYTE c = field[xwrap(teleport[level][partner(which)].x + deltax)][ywrap(teleport[level][partner(which)].y + deltay)];
if ((c < STONE || c > GOAT) && c != METAL)
return FALSE;
else return TRUE;
}
MODULE void bombblast(SBYTE triggerer, SBYTE player, SBYTE centrex, SBYTE centrey)
{ SBYTE counter, downy, downymax, leftx, leftxmax, rightx, rightxmax, strength, uppy, uppymax, x, y;
ULONG score = 0;
effect(FXUSE_BOMB);
strength = BOMBADD + (rand() % BOMBRAND);
leftxmax = centrex - strength;
if (leftxmax < 0)
leftxmax = 0;
rightxmax = centrex + strength;
if (rightxmax > FIELDX)
rightxmax = FIELDX;
uppymax = centrey - strength;
if (uppymax < 0)
uppymax = 0;
downymax = centrey + strength;
if (downymax > FIELDY)
downymax = FIELDY;
leftx = centrex;
rightx = centrex;
uppy = centrey;
downy = centrey;
for (counter = 1; counter <= strength; counter++)
{ if (leftx > leftxmax)
{ leftx--;
for (y = uppy; y <= downy; y++)
score += squareblast(triggerer, player, field[leftx][y], leftx, y, FALSE);
}
if (rightx < rightxmax)
{ rightx++;
for (y = uppy; y <= downy; y++)
score += squareblast(triggerer, player, field[rightx][y], rightx, y, FALSE);
}
if (uppy > uppymax)
{ uppy--;
for (x = leftx; x <= rightx; x++)
score += squareblast(triggerer, player, field[x][uppy], x, uppy, FALSE);
}
if (downy < downymax)
{ downy++;
for (x = leftx; x <= rightx; x++)
score += squareblast(triggerer, player, field[x][downy], x, downy, FALSE);
} }
if (triggerer == HEAD)
wormscore(player, score);
elif (triggerer == ORB)
orbscore(player, score);
if (worm[player].bias)
stat(player, LIFE);
}
MODULE void bouncegoat(SBYTE which, SBYTE x, SBYTE y)
{ if (field[x][y] == GOAT)
{ creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
orbscore(which, KILLGOAT);
change(x, y, BONUS);
} }
MODULE ABOOL bounceorb(SBYTE which, SBYTE x, SBYTE y)
{ if (field[x][y] == METAL)
return TRUE;
elif (creature[which].mode == NONE)
{ if (field[x][y] >= FIRSTNONE && field[x][y] <= LASTNONE)
return TRUE;
else return FALSE;
} elif (creature[which].mode == TONGUE)
{ if (field[x][y] >= FIRSTTONGUE && field[x][y] <= LASTTONGUE)
return TRUE;
else return FALSE;
} else
{ // assert(creature[which].mode == ARMOUR);
if (field[x][y] >= FIRSTARMOUR && field[x][y] <= LASTARMOUR)
return TRUE;
else return FALSE;
} }
MODULE SBYTE bsign(SBYTE value)
{ if (value < 0)
return (-1);
elif (value > 0)
return (1);
else return (0);
}
MODULE void changefield(void)
{ SBYTE x, y;
if (randomflag && a == PLAYGAME && level)
{ do
{ sourcelevel = (rand() % levels) + 1;
} while (randomarray[level]);
randomarray[level] = TRUE;
} else sourcelevel = level;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
field[x][y] = board[sourcelevel][x][y];
}
void clearhiscores(void)
{ SBYTE i;
clearthem = FALSE;
for (i = 0; i <= HISCORES; i++)
{ hiscore[i].player = -1;
hiscore[i].level = 0;
hiscore[i].score = 0;
hiscore[i].fresh = FALSE;
hiscore[i].name[0] = 0;
hiscore[i].time[0] = 0;
hiscore[i].date[0] = 0;
} }
MODULE void clearletters(void)
{ SBYTE player, which;
for (player = 0; player <= 3; player++)
for (which = 0; which <= LETTERS; which++)
{ letters[player][which] = FALSE;
drawletter(player, FIRSTLETTER + which, BLACK);
} }
MODULE void copyfield(SBYTE source, SBYTE destination)
{ SBYTE which, x, y;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
board[destination][x][y] = board[source][x][y];
startx[destination] = startx[source];
starty[destination] = starty[source];
for (which = 0; which <= 1; which++)
{ teleport[destination][which].alive = teleport[source][which].alive;
teleport[destination][which].x = teleport[source][which].x;
teleport[destination][which].y = teleport[source][which].y;
} }
MODULE void death(void)
{ SBYTE pain, player, which;
ABOOL slow;
for (player = 0; player <= 3; player++)
{ if (worm[player].lives)
{ if (!worm[player].alive)
{ slow = FALSE;
pain = 0;
if (worm[player].cause >= FIRSTTAIL && worm[player].cause <= LASTTAIL)
{ if (player == worm[player].cause - FIRSTTAIL)
pain = TAILPAIN;
else pain = OTHERTAILPAIN;
slow = TRUE;
} elif (worm[player].cause >= FIRSTFIRE && worm[player].cause <= LASTFIRE)
pain = WORMFIREPAIN;
elif (worm[player].cause >= FIRSTHEAD && worm[player].cause <= LASTHEAD)
pain = HEADPAIN;
elif (worm[player].cause >= FIRSTPROTECTOR && worm[player].cause <= LASTPROTECTOR)
pain = PROTECTORPAIN;
elif (worm[player].cause >= FIRSTMISSILE && worm[player].cause <= LASTMISSILE)
pain = MISSILEPAIN;
elif (worm[player].cause >= FIRSTDRIP && worm[player].cause <= LASTDRIP)
pain = DRIPPAIN;
else switch (worm[player].cause)
{
case BOMB:
pain = BOMBPAIN;
break;
case WOOD:
pain = WOODPAIN;
slow = TRUE;
break;
case FRAGMENT:
pain = FRAGMENTPAIN;
break;
case GOAT:
pain = GOATPAIN;
slow = TRUE;
break;
case OCTOPUS:
pain = OCTOPUSPAIN;
slow = TRUE;
break;
case SLAYER:
pain = SLAYERPAIN;
break;
case STONE:
pain = STONEPAIN;
slow = TRUE;
break;
case TELEPORT:
pain = TELEPORTPAIN;
slow = TRUE;
break;
case SLIME:
pain = SLIMEPAIN;
slow = TRUE;
break;
case METAL:
pain = METALPAIN;
slow = TRUE;
break;
case REMNANTS:
pain = REMNANTPAIN;
slow = TRUE;
break;
case LIGHTNING:
pain = LIGHTNINGPAIN;
break;
case PENGUIN:
pain = PENGUINPAIN;
break;
case WHIRLWIND:
pain = WHIRLWINDPAIN;
break;
case DOG:
pain = DOGPAIN;
break;
case CLOUD:
pain = CLOUDPAIN;
break;
case ORB:
pain = ORBPAIN;
break;
default:
// assert(0);
break;
}
if (worm[player].victor >= 0 && worm[player].victor != player)
{ wormscore(worm[player].victor, KILLWORM);
if (worm[worm[player].victor].bias)
{ worm[worm[player].victor].lives += pain;
stat(worm[player].victor, LIFE);
} }
if (slow)
{ worm[player].speed = slowdown(worm[player].speed, worm[player].nitro);
stat(player, NITRO);
}
if (pain > worm[player].lives)
worm[player].lives = 0;
else worm[player].lives -= pain;
draw(worm[player].x, worm[player].y, SKULL);
drawcause(player, NORMAL);
stat(player, LIFE);
if (level)
worm[player].levelreached = level;
else worm[player].levelreached = reallevel;
if (worm[player].lives)
{ effect(FXPAIN + player);
worm[player].alive = TRUE;
worm[player].causewait = r + CAUSEWAIT;
} else
{ /* kill worm */
effect(FXDEATH_WORM);
if (ice == player)
ice = -1;
field[worm[player].x][worm[player].y] = SKULL;
updatearrow(worm[player].y);
for (which = 0; which <= PROTECTORS; which++)
if (protector[player][which].alive && protector[player][which].visible)
change(protector[player][which].x, protector[player][which].y, EMPTY);
for (which = 0; which <= MAGNETS; which++)
if (magnet[which].player == player)
magnet[which].alive = FALSE;
if (worm[player].score >= worm[player].hiscore)
worm[player].hiscore = worm[player].score;
} } } }
if (!worm[0].lives && !worm[1].lives && !worm[2].lives && !worm[3].lives)
{ /* End of game */
for (player = 0; player <= 3; player++)
if (worm[player].control != NONE && worm[player].score >= worm[player].hiscore)
worm[player].hiscore = worm[player].score;
newhiscores();
effect(FXGAMEOVER);
a = GAMEOVER;
if (players == 1)
say((STRPTR) "Game over!", worm[onlyworm(FALSE)].colour);
elif (worm[0].control && ((!worm[1].control) || worm[1].score < worm[0].score) && ((!worm[2].control) || worm[2].score < worm[0].score) && ((!worm[3].control) || worm[3].score < worm[0].score))
say((STRPTR) "Green wins!", GREEN);
elif (worm[1].control && ((!worm[0].control) || worm[0].score < worm[1].score) && ((!worm[2].control) || worm[2].score < worm[1].score) && ((!worm[3].control) || worm[3].score < worm[1].score))
say((STRPTR) "Red wins!", RED);
elif (worm[2].control && ((!worm[0].control) || worm[0].score < worm[2].score) && ((!worm[1].control) || worm[1].score < worm[2].score) && ((!worm[3].control) || worm[3].score < worm[2].score))
say((STRPTR) "Blue wins!", BLUE);
elif (worm[3].control && ((!worm[0].control) || worm[0].score < worm[3].score) && ((!worm[1].control) || worm[1].score < worm[3].score) && ((!worm[2].control) || worm[2].score < worm[3].score))
say((STRPTR) "Yellow wins!", YELLOW);
else say((STRPTR) "A draw!", WHITE);
waitasec();
anykey(FALSE);
} }
MODULE void drawcause(SBYTE player, SBYTE state)
{ if (state == BLACK)
draw(-2 + ((FIELDX + 5) * worm[player].statx), (FIELDY / 2) - CAUSEYDISTANCE + (worm[player].staty * CAUSEYDISTANCE * 2), BLACKENED);
else draw(-2 + ((FIELDX + 5) * worm[player].statx), (FIELDY / 2) - CAUSEYDISTANCE + (worm[player].staty * CAUSEYDISTANCE * 2), worm[player].cause);
}
MODULE void drawletter(SBYTE player, SBYTE letter, SBYTE state)
{ UBYTE c;
if (state == BLACK)
c = BLACKENED;
else c = letter;
if (!worm[player].statx)
if (!worm[player].staty)
draw(-7 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) - 2 + ((letter - FIRSTLETTER) / 4),
c);
else
draw(-7 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) + 1 + ((letter - FIRSTLETTER) / 4),
c);
elif (!worm[player].staty)
draw(FIELDX + 5 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) - 2 + ((letter - FIRSTLETTER) / 4),
c);
else
draw(FIELDX + 5 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) + 1 + ((letter - FIRSTLETTER) / 4),
c);
}
/* NAME enginesetup -- once-only initialization of engine variables
SYNOPSIS enginesetup(void);
FUNCTION Sets up the unchanging worm variables.
MODULE engine.c */
void enginesetup(void)
{ worm[0].statx = worm[0].staty = worm[1].staty = worm[2].statx = 0;
worm[1].statx = worm[2].staty = worm[3].statx = worm[3].staty = 1;
worm[0].colour = GREEN;
worm[1].colour = RED;
worm[2].colour = BLUE;
worm[3].colour = YELLOW;
worm[0].name[0] = worm[1].name[0] = worm[2].name[0] = worm[3].name[0] = 0;
worm[0].dynamitescore =
worm[1].dynamitescore =
worm[2].dynamitescore =
worm[3].dynamitescore = 0;
strcpy(pathname, DEFAULTSET);
systemsetup();
}
/* NAME fastloop -- things done often
SYNOPSIS fastloop(void);
FUNCTION Checks for and handles level completion.
MODULE engine.c */
MODULE void fastloop(void)
{ AUTO ABOOL complete;
AUTO SBYTE advancer = -1, i, player, which, x, y;
/* update joystick `1' */
joy0();
/* update joysticks `3' and `4' */
ReadAdapterJoystick(2);
ReadAdapterJoystick(3);
if (!(rand() % octopusspeed))
{ for (i = 0; i <= CREATURES; i++)
{ if (creature[i].alive && creature[i].species == OCTOPUS && creature[i].dir >= 0)
{ octopusfire(i);
} } }
if (banging)
{ banging = FALSE;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == BANGDYNAMITE)
{ change(x, y, SILVER);
bangdynamite(x, y, infector[x][y]);
}
for (i = 0; i <= 3; i++)
if (worm[i].dynamitescore)
{ wormscore(i, worm[i].dynamitescore);
worm[i].dynamitescore = 0;
}
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == TEMPBANGDYNAMITE)
{ banging = TRUE;
field[x][y] = BANGDYNAMITE;
} }
/* DYNAMITE PHILOSOPHY:
Worm gets dynamite. Instantly the dynamite infects the surrounding
dynamite into bang-dynamite.
Each fastloop, you scan the field for bang-dynamite. For each
piece you find, it turns to silver and infects any surrounding dynamite.
flash letter */
if (level)
if (r % 8 == 1)
draw(letterx, lettery, WHITENED);
elif (r % 8 == 2)
draw(letterx, lettery, lettertype);
// flash icons
for (player = 0; player <= 3; player++)
{ icon(player, ICE);
icon(player, FREEDOM);
icon(player, CUTTER);
}
/* handle level completion */
for (player = 0; player <= 3; player++)
{ complete = TRUE;
for (which = 0; which <= LETTERS; which++)
if (!letters[player][which])
complete = FALSE;
if (complete)
advancer = player;
}
if (advancer != -1)
{ if (level++ == 0)
{ level = reallevel + 1;
reallevel = 0;
}
stopfx();
if (level > levels)
effect(FXCELEBRATE);
newlevel(advancer);
} }
MODULE ABOOL findempty(SBYTE* x, SBYTE* y, SBYTE first, SBYTE last)
{ SBYTE count = 0, xx, yy;
do
{ xx = rand() % (FIELDX + 1);
yy = rand() % (FIELDY + 1);
} while ((field[xx][yy] < first || field[xx][yy] > last) && ++count < PATIENCE);
if (count < PATIENCE)
{ *x = xx;
*y = yy;
return(TRUE);
} else return(FALSE);
}
void gameloop(void)
{ SBYTE i, player;
if (a == PLAYGAME)
{ fastloop();
gameinput();
}
if (a == PLAYGAME)
for (player = 0; player <= 3; player++)
if (worm[player].lives && (!(r % worm[player].speed)) && (ice == -1 || ice == player))
wormloop(player);
joy0();
if (a == PLAYGAME)
for (i = 0; i <= CREATURES; i++)
if (creature[i].alive && (!(r % creature[i].speed)) && (ice == -1 || (creature[i].species == MISSILE && ice == creature[i].type)))
creatureloop(i);
joy0();
if (a == PLAYGAME && !(r % MAGNETSPEED))
magnetloop();
joy0();
if (a == PLAYGAME)
death();
joy0();
if (a == PLAYGAME && !(r % VERYSLOW))
slowloop();
joy0();
timing();
}
MODULE void killall(void)
{ UBYTE i;
for (i = 0; i <= CREATURES; i++)
creature[i].alive = FALSE;
for (i = 0; i <= MAGNETS; i++)
magnet[i].alive = FALSE;
teleport[level][2].alive = FALSE;
teleport[level][3].alive = FALSE;
}
void levelappend(void)
{ UBYTE oldlevel;
if (levels < MAXLEVELS)
{ oldlevel = level;
level = ++levels;
newfield();
level = oldlevel;
saylevel(WHITE);
} }
void leveldelete(void)
{ SBYTE i;
/* pull boards */
if (levels > 1)
{ if (level < levels)
for (i = level; i < levels; i++)
copyfield(i + 1, i);
else
level--;
levels--;
saylevel(WHITE);
turborender();
} }
void levelerase(void)
{ newfield();
turborender();
}
void levelinsert(void)
{ SBYTE i;
/* push boards
*/
if (levels < MAXLEVELS)
{ for (i = levels; i >= level; i--)
copyfield(i, i + 1);
levels++;
saylevel(WHITE);
newfield();
turborender();
} }
SBYTE loadfields(STRPTR fieldname)
{ SBYTE i, j, x, y;
TEXT IOBuffer[NAMELENGTH + 1];
UBYTE ver;
/* This routine is not entirely robust, especially regarding
failures part way through reading. Also, field data values must be
those supported by the field editor (ie. objects, and the squares
represented by F1-F8), or undefined behaviour may result. None of
this is currently checked for. Provided that the fieldset was
created with the official field editor, and the file is not
corrupt, these failures should never happen anyway.
open file */
// say("Opening...", WHITE);
if (!ZOpen(fieldname, FALSE))
return 1; /* no harm done */
/* read header */
// say("Reading header...", WHITE);
if (!ZRead(IOBuffer, 10))
{ ZClose();
return 2; /* no harm done */
}
if (!strcmp(IOBuffer, "FSET 6.6"))
ver = 66;
elif (!strcmp(IOBuffer, "FSET 6.3"))
ver = 63;
elif (!strcmp(IOBuffer, "FSET 6.2"))
ver = 62;
elif (!strcmp(IOBuffer, "FSET 6.0"))
ver = 60;
elif (!strcmp(IOBuffer, "FSET 5.6"))
ver = 56;
elif (!strcmp(IOBuffer, "FSET 5.5"))
ver = 55;
elif (!strcmp(IOBuffer, "FSET 5.3"))
ver = 53;
elif (!strcmp(IOBuffer, "FSET 5.1"))
ver = 51;
elif (!strcmp(IOBuffer, "FSET 5.0"))
ver = 50;
else
{ ZClose();
return 3; /* no harm done */
}
levels = IOBuffer[9];
/* read high score table */
// say("Reading high score table...", WHITE);
for (i = 0; i <= HISCORES; i++)
{ if (!ZRead(IOBuffer, 6))
{ ZClose();
return 4; /* incorrect levels */
}
hiscore[i].fresh = FALSE;
hiscore[i].player = IOBuffer[0];
hiscore[i].level = IOBuffer[1];
hiscore[i].score = (IOBuffer[2] * 16777216)
+ (IOBuffer[3] * 65536)
+ (IOBuffer[4] * 256)
+ IOBuffer[5];
if (!ZRead(IOBuffer, NAMELENGTH + 1))
{ ZClose();
return 5; /* incorrect levels, corrupted high scores */
}
for (j = 0; j <= NAMELENGTH; j++)
{ hiscore[i].name[j] = IOBuffer[j];
}
if (!ZRead(IOBuffer, TIMELENGTH + 1))
{ ZClose();
return 6; /* incorrect levels, corrupted high scores */
}
for (j = 0; j <= TIMELENGTH; j++)
{ hiscore[i].time[j] = IOBuffer[j];
}
if (!ZRead(IOBuffer, DATELENGTH + 1))
{ ZClose();
return 7; /* incorrect levels, corrupted high scores */
}
for (j = 0; j <= DATELENGTH; j++)
{ hiscore[i].date[j] = IOBuffer[j];
} }
// say("Reading level data...", WHITE);
/* read level data */
for (i = 0; i <= levels; i++)
{ if (!ZRead(IOBuffer, 8))
{ ZClose();
return 9;
/* incorrect levels, corrupted high scores,
incorrect startx, teleports, field data */
}
startx[i] = IOBuffer[0];
starty[i] = IOBuffer[1];
teleport[i][0].alive = IOBuffer[2];
teleport[i][0].x = IOBuffer[3];
teleport[i][0].y = IOBuffer[4];
teleport[i][1].alive = IOBuffer[5];
teleport[i][1].x = IOBuffer[6];
teleport[i][1].y = IOBuffer[7];
if (!ZRead((char *) &board[i][0][0], LEVELSIZE))
{ ZClose();
return 10;
/* incorrect levels, corrupted high scores,
incorrect startx, teleports, field data */
} else
{ if (ver <= 50)
{ // convert from FSET 5.0 to FSET 5.1 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= 11)
board[i][x][y]++;
}
if (ver <= 51)
{ // convert from FSET 5.1 to FSET 5.3 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= 7 && board[i][x][y] <= 63)
board[i][x][y]++;
}
if (ver <= 53)
{ // convert from FSET 5.3 to FSET 5.5 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= 20)
board[i][x][y]++;
}
if (ver <= 55)
{ // convert from FSET 5.5 to FSET 5.6 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= 13)
board[i][x][y]++;
}
if (ver <= 56)
{ // convert from FSET 5.6 to FSET 6.0 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= 8 && board[i][x][y] <= 20)
board[i][x][y]++;
elif (board[i][x][y] >= 21)
board[i][x][y] += 2;
}
if (ver <= 60)
{ // convert from FSET 6.0 to FSET 6.2 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= 7)
board[i][x][y]++;
}
if (ver <= 62)
{ // convert from FSET 6.2 to FSET 6.3/6.6 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= 7)
board[i][x][y]++;
} } }
// say("Open done.", WHITE);
// no need to read version string
ZClose();
modified = FALSE;
return 0;
}
MODULE void magnetloop(void)
{ SBYTE i;
UBYTE c;
for (i = 0; i <= MAGNETS; i++)
{ if (magnet[i].alive)
{ // defensive programming to ensure magnet is still valid
if (field[magnet[i].x][magnet[i].y] != magnet[i].object)
{ magnet[i].alive = FALSE;
} else
{ change(magnet[i].x, magnet[i].y, EMPTY);
magnet[i].x += bsign(worm[magnet[i].player].x - magnet[i].x);
magnet[i].y += bsign(worm[magnet[i].player].y - magnet[i].y);
c = field[magnet[i].x][magnet[i].y];
if ((c >= FIRSTEMPTY && c <= LASTEMPTY)
|| (c >= FIRSTTAIL && c <= LASTTAIL))
change(magnet[i].x, magnet[i].y, magnet[i].object);
elif (c >= FIRSTHEAD && c <= LASTHEAD)
{ change(magnet[i].x, magnet[i].y, magnet[i].object);
wormscore(c - FIRSTHEAD, wormobject(c - FIRSTHEAD, magnet[i].x, magnet[i].y));
change(magnet[i].x, magnet[i].y, FIRSTHEAD + magnet[i].player); // not entirely the right head image
} else magnet[i].alive = FALSE;
} } } }
AGLOBAL void matchteleports(void)
{ AUTO UWORD teleports, octopi;
AUTO SBYTE x, y, i;
say("Checking fieldset...", WHITE);
for (i = 0; i <= levels; i++)
{ teleports = octopi = 0;
teleport[level][0].alive = teleport[level][1].alive = FALSE;
for (y = 0; y <= FIELDY; y++)
{ for (x = 0; x <= FIELDX; x++)
{ if (board[level][x][y] == TELEPORT)
{ teleports++;
if (teleports <= 2)
{ teleport[level][teleports - 1].x = x;
teleport[level][teleports - 1].y = y;
} else
{ board[level][x][y] = EMPTY;
} }
elif (board[level][x][y] == OCTOPUS)
{ octopi++;
if (octopi > 12)
{ board[level][x][y] = EMPTY;
} } } }
if (teleports == 1)
{ board[level][teleport[level][0].x][teleport[level][0].y] = EMPTY;
} elif (teleports >= 2)
{ teleport[level][0].alive = teleport[level][1].alive = TRUE;
} } }
MODULE void newfield(void)
{ SBYTE x, y;
teleport[level][0].alive = FALSE;
teleport[level][1].alive = FALSE;
startx[level] = FIELDX / 2;
starty[level] = FIELDY / 2;
if (level)
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
board[level][x][y] = EMPTY;
else for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
board[0][x][y] = SILVER;
}
void newfields(void)
{ if (verify())
{ strcpy(pathname, DEFAULTSET);
levels = DEFAULTLEVELS;
modified = FALSE;
for (level = 0; level <= levels; level++)
newfield();
clearhiscores();
level = 1;
if (a == FIELDEDIT)
{ turborender();
saylevel(WHITE);
} else hiscores();
} }
void newgame(void)
{ SBYTE i, player;
players = 0;
for (player = 0; player <= 3; player++)
{ if (worm[player].control != NONE)
players++;
worm[player].lives = 0;
worm[player].speed = NORMAL;
worm[player].hiscore = 0;
}
for (i = 1; i <= MAXLEVELS; i++)
randomarray[i] = FALSE;
r = -1;
trainer = FALSE;
ice = -1;
reallevel = 0;
level = 1;
a = PLAYGAME;
clearscreen();
newlevel(rand() % 4);
timing();
}
MODULE void newhiscores(void)
{ PERSIST TEXT amiganame[4][NAMELENGTH + 1] = {"Jay Miner", "Carl Sassenrath", "R. J. Mical", "Dave Morse"};
AUTO SBYTE i, j, player;
datestamp();
for (player = 0; player <= 3; player++)
for (i = 0; i <= HISCORES; i++)
if (worm[player].control != NONE && worm[player].score >= hiscore[i].score)
{ /* push all worse hiscores down */
if (i < HISCORES)
for (j = HISCORES; j >= i + 1; j--)
{ hiscore[j].player = hiscore[j - 1].player;
hiscore[j].level = hiscore[j - 1].level;
hiscore[j].score = hiscore[j - 1].score;
hiscore[j].fresh = hiscore[j - 1].fresh;
strcpy(hiscore[j].name, hiscore[j - 1].name);
strcpy(hiscore[j].date, hiscore[j - 1].date);
strcpy(hiscore[j].time, hiscore[j - 1].time);
}
modified = TRUE;
hiscore[i].player = player;
hiscore[i].level = worm[player].levelreached;
hiscore[i].score = worm[player].hiscore;
if (worm[player].control == AMIGA)
{ strcpy(hiscore[i].name, amiganame[player]);
hiscore[i].fresh = FALSE;
} else
{ strcpy(hiscore[i].name, "(New)");
hiscore[i].fresh = TRUE;
}
strcpy(hiscore[i].time, times);
strcpy(hiscore[i].date, date);
break; /* vital */
} }
MODULE void newlevel(SBYTE player)
{ SBYTE i, j;
UWORD octopi = 0;
UBYTE x, y;
if (level >= 2)
rundown(player);
if (a == PLAYGAME)
{ if (level > levels)
{ for (i = 0; i <= 3; i++)
{ if (worm[i].lives)
worm[i].levelreached = -1;
if (worm[player].control != NONE && worm[player].score >= worm[player].hiscore)
worm[player].hiscore = worm[player].score;
}
celebrate();
newhiscores();
titlescreen();
} else
{ saylevel(WHITE);
for (i = 0; i <= 3; i++)
{ worm[i].multi = (SBYTE) atleast(worm[i].multi / 2, 1);
worm[i].remnants = FALSE;
worm[i].pusher = FALSE;
}
killall();
clearletters();
changefield();
octopusspeed = FAST;
for (x = 0; x <= FIELDX; x++)
{ for (y = 0; y <= FIELDY; y++)
{ if (board[level][x][y] == OCTOPUS)
{ octopi++;
if (octopi <= 12)
{ creature[octopi].alive = TRUE;
creature[octopi].species = OCTOPUS;
creature[octopi].visible = TRUE;
creature[octopi].x = x;
creature[octopi].y = y;
creature[octopi].speed = VERYSLOW;
creature[octopi].dir = -1;
creature[octopi].deltax = creature[octopi].deltay = 0;
} } } }
for (i = 0; i <= 3; i++)
{ worm[i].speed = NORMAL;
if (worm[i].lives)
stat(i, NITRO);
worm[i].moved = FALSE;
worm[i].x = startx[sourcelevel];
worm[i].y = starty[sourcelevel];
worm[i].arrowy = worm[i].y;
switch(i)
{
case 0:
worm[0].deltax = -1;
worm[0].deltay = 0;
break;
case 1:
worm[1].deltax = 1;
worm[1].deltay = 0;
break;
case 2:
worm[2].deltax = 0;
worm[2].deltay = -1;
break;
case 3:
worm[3].deltax = 0;
worm[3].deltay = 1;
break;
default:
break;
} }
turborender();
delay = atleast(DELAY_MAX - (level * DELAY_DEC), DELAY_MIN);
if (level)
{ secondsperlevel = SECONDSPERLEVEL;
putletter(-1);
freq = (SBYTE) atleast(FREQ_MAX - (level * FREQ_DEC), FREQ_MIN);
for (i = 0; i <= 3; i++)
{ if (!worm[i].lives && worm[i].control != NONE)
{ /* create (or resurrect) a worm */
worm[i].lives = STARTLIVES;
worm[i].score = 0;
worm[i].oldscore = 0;
worm[i].armour = 0;
worm[i].tongue = 0;
worm[i].alive = TRUE;
worm[i].nitro = FALSE;
worm[i].flashed = FALSE;
worm[i].mode = NOMODE;
worm[i].power = 0;
worm[i].bias = 0;
worm[i].multi = 1;
worm[i].ice = 0;
worm[i].victor = -1;
worm[i].ammo = 0;
worm[i].remnants = FALSE;
worm[i].affixer = FALSE;
worm[i].sideshot = FALSE;
worm[i].pusher = FALSE;
worm[i].rammed = FALSE;
worm[i].freedom = 0;
worm[i].causewait = (ULONG) -1;
worm[i].last = FIRSTTAIL + i;
worm[i].pos = -1;
worm[i].cutter = 0;
for (j = 0; j <= PROTECTORS; j++)
protector[i][j].alive = FALSE;
for (j = 0; j <= LASTOBJECT; j++)
stat(i, j);
} } }
icon(i, ICE);
icon(i, REMNANTS);
icon(i, AFFIXER);
icon(i, SIDESHOT);
icon(i, PUSHER);
icon(i, FREEDOM);
icon(i, CUTTER);
} }
clearjoystick();
clearkybd();
resettime();
}
MODULE void explosion(SBYTE x, SBYTE y, SBYTE exceptionx, SBYTE exceptiony, UBYTE creator)
{ UBYTE i, generated = 0;
effect(FXGET_PULSE);
for (i = 0; i <= CREATURES; i++)
{ if ((!(creature[i].alive)) && generated <= 7)
{ if (creator == OCTOPUS)
{ creature[i].last = OCTOPUS;
creature[i].visible = FALSE;
} else
{ creature[i].last = EMPTY;
creature[i].visible = TRUE;
}
creature[i].x = x;
creature[i].y = y;
if (level)
{ creature[i].speed = (SBYTE) atleast(FRAGSPEED - (level / 2), 1);
} else creature[i].speed = BONUSFRAGSPEED;
creature[i].species = FRAGMENT;
switch (generated)
{
case 0:
creature[i].deltax = 0;
creature[i].deltay = -1;
break;
case 1:
creature[i].deltax = 1;
creature[i].deltay = -1;
break;
case 2:
creature[i].deltax = 1;
creature[i].deltay = 0;
break;
case 3:
creature[i].deltax = 1;
creature[i].deltay = 1;
break;
case 4:
creature[i].deltax = 0;
creature[i].deltay = 1;
break;
case 5:
creature[i].deltax = -1;
creature[i].deltay = 1;
break;
case 6:
creature[i].deltax = -1;
creature[i].deltay = 0;
break;
case 7:
creature[i].deltax = -1;
creature[i].deltay = -1;
break;
default:
break;
}
generated++;
if (creature[i].deltax != exceptionx || creature[i].deltay != exceptiony)
{ creature[i].alive = TRUE;
if (generated == 1 && creator != OCTOPUS)
{ change(x, y, FRAGMENT);
} } } } }
/* Many creatures take advantage of shared characteristics.
11 creature types (orbs, goats, drips, fragments, missiles, penguins,
cyclones, dogs, clouds, timebombs, octopus) use the creature structure.
Independent of it are worms, protectors, worm bullets and teleports. */
MODULE void creatureloop(SBYTE which)
{ ABOOL happy = FALSE;
UBYTE bestdistance = 255, distance, player, c, i;
SBYTE x, y, xx, yy, xxx, yyy, frontx, fronty, rearx, reary;
x = creature[which].x;
y = creature[which].y;
if (!valid(x, y)) // defensive programming
{ creature[which].alive = FALSE;
return;
/* TEXT temp1[SAYLIMIT + 1], temp2[8];
strcpy(temp1, "BAD CREATURE AT x: ");
stci_d(temp2, x);
strcat(temp1, temp2);
strcat(temp1, ", y: ");
stci_d(temp2, y);
strcat(temp1, temp2);
strcat(temp1, "!");
say(temp1, PURPLE);
draw(FIELDX + 1, 0, creature[which].species); // indicates which creature
Delay(250);
clearkybd();
anykey(FALSE); */
}
if (creature[which].species == ORB && creature[which].explode)
{ creature[which].alive = FALSE;
explosion(x, y, -2, -2, ORB);
return;
}
/* decide whether and where to move */
switch(creature[which].species)
{
case CLOUD:
if (creature[which].x == 0 || creature[which].x == FIELDX)
creature[which].deltax = -creature[which].deltax;
break;
case TIMEBOMB:
/* decrement and explode timebombs */
if (field[x][y] != TIMEBOMB)
creature[which].alive = FALSE;
else
{ effect(FXDO_BOMB);
creature[which].time--;
if (creature[which].time < 0)
{ creature[which].alive = FALSE;
bombblast(BOMB, 0, x, y);
change(x, y, EMPTY);
} else draw(x, y, ZERO + creature[which].time);
}
return; // not a bug
break;
case DOG:
/* remove a movement from the dog queue */
if (creature[which].dormant == CHASING)
{ if (creature[which].pos != -1)
{ creature[which].deltax = thedogqueue[which][0].deltax;
creature[which].deltay = thedogqueue[which][0].deltay;
if (--creature[which].pos != -1)
{ for (i = 0; i <= creature[which].pos; i++)
{ thedogqueue[which][i].deltax = thedogqueue[which][i + 1].deltax;
thedogqueue[which][i].deltay = thedogqueue[which][i + 1].deltay;
} } }
else creature[which].alive = FALSE;
}
break;
case PENGUIN:
do
{ xx = (rand() % 3) - 1;
yy = (rand() % 3) - 1;
} while (!valid(x + xx, y + yy));
c = field[x + xx][y + yy];
if (c >= FIRSTEMPTY && c <= LASTEMPTY)
{ creature[which].deltax = xx;
creature[which].deltay = yy;
} else
{ creature[which].deltax = 0;
creature[which].deltay = 0;
}
break;
case WHIRLWIND:
/* Whirlwinds has a slight upwards drift.
Higher values of UNDRIFT make it less buoyant. */
creature[which].deltax = (rand() % 3) - 1;
if (!(rand() % UNDRIFT))
creature[which].deltay = (rand() % 2) - 1;
else creature[which].deltay = (rand() % 3) - 1;
break;
case MISSILE:
for (player = 0; player <= 3; player++)
{ if (creature[which].type != player && worm[player].lives && (!worm[player].bias))
{ xx = abs(worm[player].x - x);
yy = abs(worm[player].y - y);
if (xx < yy)
distance = xx;
else distance = yy;
if (distance <= bestdistance)
{ bestdistance = distance;
creature[which].deltax = bsign(worm[player].x - x);
creature[which].deltay = bsign(worm[player].y - y);
} }
for (i = 0; i <= CREATURES; i++)
if (creature[i].alive && which != i)
{ if
( creature[i].species == ORB
|| (creature[i].species == MISSILE && creature[i].type != creature[which].type)
|| creature[i].species == GOAT
)
{ xx = abs(creature[i].x - x);
yy = abs(creature[i].y - y);
if (xx < yy)
distance = xx;
else distance = yy;
if (distance <= bestdistance)
{ bestdistance = distance;
creature[which].deltax = bsign(creature[i].x - x);
creature[which].deltay = bsign(creature[i].y - y);
} } } }
if (bestdistance == 255)
creature[which].alive = FALSE;
break;
case ORB:
frontx = xwrap(x + creature[which].deltax); /* look in front */
fronty = ywrap(y + creature[which].deltay);
rearx = xwrap(x - creature[which].deltax); /* look behind */
reary = ywrap(y - creature[which].deltay);
if (bounceorb(which, frontx, fronty))
{ bouncegoat(which, frontx, fronty);
xx = -creature[which].deltax; /* default bounce angle is 180° */
yy = -creature[which].deltay;
if (!bounceorb(which, frontx, reary))
{ if (bounceorb(which, rearx, fronty))
{ bouncegoat(which, rearx, fronty);
xx = creature[which].deltax;
} }
elif (!bounceorb(which, rearx, fronty))
{ bouncegoat(which, rearx, fronty);
yy = creature[which].deltay;
}
creature[which].deltax = xx;
creature[which].deltay = yy;
}
break;
case GOAT:
/* decide whether to move */
if (!(rand() % GOATMOVE))
{ for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy) && field[xx][yy] >= FIRSTEMPTY && field[xx][yy] <= LASTEMPTY)
{ happy = TRUE;
break;
} }
creature[which].deltax = 0;
creature[which].deltay = 0;
if (!happy)
{ xx = (rand() % 3) - 1;
yy = (rand() % 3) - 1;
if (valid(x + xx, y + yy) && (xx || yy))
{ c = field[x + xx][y + yy];
if (c >= FIRSTGOAT && c <= LASTGOAT && c != GOAT)
{ creature[which].last = creature[which].oldlast;
creature[which].oldlast = c;
creature[which].deltax = xx;
creature[which].deltay = yy;
} } }
break;
default:
break;
}
/* now move */
if (creature[which].deltax || creature[which].deltay)
{ if (creature[which].visible)
{ /* erase previous image */
change(x, y, creature[which].last);
creature[which].last = EMPTY;
}
if (creature[which].alive)
{ creature[which].x += creature[which].deltax;
creature[which].y += creature[which].deltay;
if (creature[which].species == ORB)
{ creature[which].x = xwrap(creature[which].x);
creature[which].y = ywrap(creature[which].y);
} elif (creature[which].species == FRAGMENT || creature[which].species == DRIP || creature[which].species == WHIRLWIND || creature[which].species == DOG)
{ if (!valid(creature[which].x, creature[which].y))
creature[which].alive = FALSE;
} } }
creature[which].visible = TRUE;
x = creature[which].x;
y = creature[which].y;
/* Collision detection.
Timebombs don't need to go through this. */
if
( creature[which].alive
&& creature[which].species != GOAT
&& creature[which].species != PENGUIN
&& creature[which].species != OCTOPUS
&& (creature[which].deltax || creature[which].deltay)
)
{ c = field[x][y];
if (c >= FIRSTHEAD && c <= LASTHEAD)
{ if (creature[which].species == MISSILE)
wormmissile(x, y, c - FIRSTHEAD, which);
elif (creature[which].species == DOG)
wormdog(x, y, c - FIRSTHEAD, which);
elif (creature[which].species == FRAGMENT)
wormfrag(x, y, c - FIRSTHEAD, which);
elif (creature[which].species == DRIP)
wormdrip(x, y, c - FIRSTHEAD, which);
elif (creature[which].species == WHIRLWIND)
wormwhirlwind(x, y, c - FIRSTHEAD, which);
elif (creature[which].species == CLOUD)
cloudworm(x, y, which, c - FIRSTHEAD);
elif (creature[which].species == ORB)
{ wormorb(x, y, c - FIRSTHEAD, which);
creature[which].last = c - FIRSTHEAD + FIRSTTAIL; /* note sign issues */
} }
elif (c == TIMEBOMB)
{ creature[whichcreature(x, y, TIMEBOMB, which)].alive = FALSE;
bombblast(BOMB, 0, x, y);
change(x, y, EMPTY);
} elif (c == OCTOPUS)
{ if (creature[which].species == ORB)
{ effect(FXDEATH_ORB);
} elif (creature[which].species == FRAGMENT)
{ effect(FXDEATH_FRAGMENT);
}
creature[which].alive = FALSE;
} elif (c >= FIRSTTAIL && c <= LASTTAIL)
{ if (creature[which].species == ORB)
{ if (creature[which].mode == TONGUE)
effect(FXUSE_TONGUE);
else
{ if (worm[c - FIRSTTAIL].alive)
{ effect(FXDEATH_ORB);
wormscore(c - FIRSTTAIL, creature[which].score);
if (worm[c - FIRSTTAIL].bias)
{ worm[c - FIRSTTAIL].lives += ORBBLOOD;
stat(c - FIRSTTAIL, LIFE);
} }
creature[which].alive = FALSE;
change(x, y, BONUS);
} } }
elif (c >= FIRSTPROTECTOR && c <= LASTPROTECTOR)
{ if (creature[which].species == DOG)
protdog(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == DRIP)
protdrip(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == MISSILE)
protmissile(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == FRAGMENT)
protfrag(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == ORB)
protorb(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == PENGUIN)
protpenguin(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == WHIRLWIND)
protwhirlwind(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == CLOUD)
cloudprot(x, y, which, c - FIRSTPROTECTOR);
} elif (c >= FIRSTLETTER && c <= LASTLETTER)
{ if (creature[which].species == DRIP || creature[which].species == FRAGMENT || creature[which].species == MISSILE || creature[which].species == CLOUD)
{ effect(FXDEATH_FRAGMENT);
creature[which].alive = FALSE;
} elif (creature[which].species == ORB)
{ for (player = 0; player <= 3; player++)
{ letters[player][c - FIRSTLETTER] = FALSE;
drawletter(player, c, BLACK);
}
putletter(-1);
orbscore(which, LETTERPOINT);
} elif (creature[which].species == WHIRLWIND)
putletter(-1);
} elif (c >= FIRSTDRIP && c <= LASTDRIP)
{ i = whichcreature(x, y, DRIP, which);
if (creature[which].species == FRAGMENT)
dripfrag(x, y, i, which);
elif (creature[which].species == MISSILE)
dripmissile(x, y, i, which);
elif (creature[which].species == ORB)
driporb(x, y, i, which);
elif (creature[which].species == WHIRLWIND)
dripwhirlwind(x, y, i, which);
elif (creature[which].species == CLOUD)
clouddrip(x, y, which, i);
} elif (c >= FIRSTMISSILE && c <= LASTMISSILE)
{ i = whichcreature(x, y, MISSILE, which);
if (creature[which].species == FRAGMENT)
fragmissile(x, y, which, i);
elif (creature[which].species == DRIP)
dripmissile(x, y, which, i);
elif (creature[which].species == ORB)
orbmissile(x, y, which, i);
elif (creature[which].species == WHIRLWIND)
missilewhirlwind(x, y, i, which);
elif (creature[which].species == MISSILE)
missilemissile(x, y, which, i);
elif (creature[which].species == CLOUD)
cloudmissile(x, y, which, i);
} elif (c == WHIRLWIND)
{ i = whichcreature(x, y, WHIRLWIND, which);
if (creature[which].species == DRIP)
dripwhirlwind(x, y, which, i);
elif (creature[which].species == MISSILE)
missilewhirlwind(x, y, which, i);
elif (creature[which].species == ORB)
orbwhirlwind(x, y, which, i);
elif (creature[which].species == WHIRLWIND)
whirlwindwhirlwind(x, y, which, i);
elif (creature[which].species == FRAGMENT)
fragwhirlwind(x, y, which, i);
elif (creature[which].species == CLOUD)
cloudwhirlwind(x, y, which, i);
} elif (c == DOG)
{ i = whichcreature(x, y, DOG, which);
if (creature[which].species == FRAGMENT)
dogfrag(x, y, i, which);
elif (creature[which].species == PENGUIN)
dogpenguin(x, y, i, which);
elif (creature[which].species == ORB)
dogorb(x, y, i, which);
elif (creature[i].species == DRIP)
dogdrip(x, y, i, which);
elif (creature[i].species == WHIRLWIND)
dogwhirlwind(x, y, i, which);
elif (creature[i].species == MISSILE)
dogmissile(x, y, i, which);
elif (creature[i].species == DOG)
dogdog(x, y, i, which);
elif (creature[i].species == CLOUD)
clouddog(x, y, which, i);
} elif (c == PENGUIN)
{ i = whichcreature(x, y, PENGUIN, which);
if (creature[which].species == ORB)
orbpenguin(x, y, which, i);
elif (creature[which].species == DOG)
dogpenguin(x, y, which, i);
elif (creature[which].species == WHIRLWIND)
penguinwhirlwind(x, y, i, which);
elif (creature[which].species == MISSILE)
missilepenguin(x, y, which, i);
elif (creature[which].species == DRIP)
drippenguin(x, y, which, i);
elif (creature[which].species == CLOUD)
cloudpenguin(x, y, which, i);
elif (creature[which].species == FRAGMENT)
fragpenguin(x, y, which, i);
} elif (c == FRAGMENT)
{ i = whichcreature(x, y, FRAGMENT, which);
if (creature[which].species == MISSILE)
fragmissile(x, y, i, which);
elif (creature[which].species == DRIP)
dripfrag(x, y, which, i);
elif (creature[which].species == ORB)
fragorb(x, y, i, which);
elif (creature[which].species == FRAGMENT)
fragfrag(x, y, which, i);
elif (creature[which].species == WHIRLWIND)
fragwhirlwind(x, y, i, which);
elif (creature[which].species == CLOUD)
cloudfrag(x, y, which, i);
elif (creature[which].species == DOG)
dogfrag(x, y, which, i);
} elif (c == METAL)
{ if (creature[which].species == DRIP || creature[which].species == MISSILE || creature[which].species == CLOUD)
creature[which].alive = FALSE;
elif (creature[which].species == FRAGMENT)
reflect(which);
} elif (c == STONE)
{ if (creature[which].species == DRIP || creature[which].species == FRAGMENT || creature[which].species == MISSILE || creature[which].species == CLOUD)
{ effect(FXDEATH_FRAGMENT);
creature[which].alive = FALSE;
} }
elif (c == WOOD)
{ if (creature[which].species == FRAGMENT || creature[which].species == MISSILE || creature[which].species == CLOUD || creature[which].species == DRIP)
{ effect(FXDEATH_FRAGMENT);
creature[which].alive = FALSE;
} }
elif (c == GOAT)
{ /* Drips, fragments, missiles, cyclones, dogs, clouds */
creature[whichcreature(x, y, GOAT, which)].alive = FALSE;
effect(FXDEATH_GOAT);
if (creature[which].species != WHIRLWIND)
{ creature[which].alive = FALSE;
change(x, y, BONUS);
}
if (creature[which].species == MISSILE)
{ wormscore(creature[which].type, KILLGOAT);
if (worm[creature[which].type].bias)
{ worm[creature[which].type].lives += GOATBLOOD;
stat(creature[which].type, LIFE);
} } }
elif (c == SLIME)
{ if (creature[which].species == DRIP || creature[which].species == FRAGMENT || creature[which].species == MISSILE || (creature[which].species == ORB && creature[which].mode != ARMOUR))
{ creature[which].alive = FALSE;
change(x, y, EMPTY);
} }
elif (c == DYNAMITE)
{ if (creature[which].species == ORB)
{ orbscore(which, DYNAMITEPOINT);
} }
elif (c == SKULL)
{ if (creature[which].species == ORB)
{ effect(FXGET_SKULL);
orbscore(which, SKULLPOINT);
} elif (creature[which].species == FRAGMENT || creature[which].species == DRIP || creature[which].species == MISSILE || creature[which].species == CLOUD)
{ effect(FXDEATH_FRAGMENT);
creature[which].alive = FALSE;
} }
elif (c == TELEPORT)
{ /* Drips, fragments, missiles, orbs, whirlwinds, dogs, clouds */
i = whichteleport(x, y);
if (blocked(i, creature[which].deltax, creature[which].deltay))
creature[which].alive = FALSE;
else
{ effect(FXUSE_TELEPORT);
creature[which].x = teleport[level][partner(i)].x + creature[which].deltax;
creature[which].y = teleport[level][partner(i)].y + creature[which].deltay;
if (creature[which].species == ORB)
{ orbscore(which, TELPOINT);
creature[which].x = xwrap(creature[which].x);
creature[which].y = ywrap(creature[which].y);
} else
{ if (!(valid(creature[which].x, creature[which].y)))
creature[which].alive = FALSE;
if (creature[which].species == FRAGMENT)
creature[which].last = SILVER;
} } }
elif (c == ORB)
{ i = whichcreature(x, y, ORB, which);
if (creature[which].species == DRIP)
driporb(x, y, which, i);
elif (creature[which].species == FRAGMENT)
fragorb(x, y, which, i);
elif (creature[which].species == WHIRLWIND)
orbwhirlwind(x, y, i, which);
elif (creature[which].species == PENGUIN)
orbpenguin(x, y, i, which);
elif (creature[which].species == MISSILE)
orbmissile(x, y, i, which);
elif (creature[which].species == DOG)
dogorb(x, y, which, i);
elif (creature[which].species == ORB)
orborb(x, y, which, i);
elif (creature[which].species == CLOUD)
cloudorb(x, y, which, i);
} elif (c == CLOUD)
{ i = whichcreature(x, y, CLOUD, which);
if (creature[which].species == DRIP)
clouddrip(x, y, which, i);
elif (creature[which].species == FRAGMENT)
cloudfrag(x, y, which, i);
elif (creature[which].species == WHIRLWIND)
cloudwhirlwind(x, y, which, i);
elif (creature[which].species == PENGUIN)
cloudpenguin(x, y, which, i);
elif (creature[which].species == MISSILE)
cloudmissile(x, y, which, i);
elif (creature[which].species == DOG)
clouddog(x, y, which, i);
elif (creature[which].species == ORB)
cloudorb(x, y, which, i);
elif (creature[which].species == CLOUD)
cloudcloud(x, y, which, i);
} elif (creature[which].species == ORB)
{ if (c <= LASTOBJECT)
{ orbscore(which, object[c].score);
if (c == AMMO || c == PULSE || c == SLAYER || c == LIGHTNING)
{ if (creature[which].mode != ARMOUR)
creature[which].explode = TRUE;
else effect(FXUSE_ARMOUR);
} elif (c == NITRO || c == POWER || c == SLOWER || c == CYCLONE)
{ effect(FXGET_NITRO);
creature[which].speed = speedup(creature[which].speed, TRUE);
} elif (c == HEALER || c == LIFE || c == ICE || c == TREASURE || c == UMBRELLA || c == BONUS)
orbsplit(which);
else switch (c)
{
case BOMB:
draw(x, y, ORB);
bombblast(ORB, which, x, y);
break;
case ARMOUR:
creature[which].armour += MODEADD + (rand() % MODERAND);
creature[which].mode = ARMOUR;
break;
case TONGUE:
creature[which].tongue += MODEADD + (rand() % MODERAND);
creature[which].mode = TONGUE;
break;
case PROTECTOR:
for (player = 0; player <= 3; player++)
if (worm[player].lives)
for (i = 0; i <= PROTECTORS; i++)
if (protector[player][i].alive)
{ protector[player][i].alive = FALSE;
if (protector[player][i].visible)
change(protector[player][i].x, protector[player][i].y, EMPTY);
}
break;
case MISSILE:
case CONVERTER:
effect(FXGET_OBJECT);
for (i = 0; i <= CREATURES; i++)
{ if (creature[i].alive && creature[i].species == MISSILE)
{ creature[i].alive = FALSE;
change(x, y, EMPTY);
} }
break;
case MULTIPLIER:
effect(FXGET_OBJECT);
creature[which].multi *= 2;
if (creature[which].multi > MULTILIMIT)
creature[which].multi = MULTILIMIT;
break;
case BIAS:
effect(FXGET_OBJECT);
for (player = 0; player <= 3; player++)
if (worm[player].lives && worm[player].bias)
{ worm[player].bias = 0;
stat(player, BIAS);
}
break;
case AFFIXER:
effect(FXGET_OBJECT);
for (player = 0; player <= 3; player++)
if (worm[player].lives)
{ worm[player].affixer = FALSE;
icon(player, AFFIXER);
}
break;
case REMNANTS:
effect(FXGET_OBJECT);
for (player = 0; player <= 3; player++)
if (worm[player].lives)
{ worm[player].remnants = FALSE;
icon(player, REMNANTS);
}
break;
case SIDESHOT:
effect(FXGET_POWERUP);
for (player = 0; player <= 3; player++)
if (worm[player].lives)
{ worm[player].sideshot = FALSE;
icon(player, SIDESHOT);
}
break;
case MAGNET:
effect(FXGET_OBJECT);
for (i = 0; i <= MAGNETS; i++)
if (magnet[i].alive)
magnet[i].alive = FALSE;
break;
case PUSHER:
effect(FXGET_OBJECT);
for (i = 0; i <= 3; i++)
if (worm[i].lives && worm[i].pusher)
{ worm[i].pusher = FALSE;
icon(i, PUSHER);
}
break;
case FREEDOM:
effect(FXGET_OBJECT);
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] >= FIRSTFIRE && field[xx][yy] <= LASTFIRE)
change(xx, yy, EMPTY);
break;
case SWITCHER:
effect(FXGET_OBJECT);
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] >= FIRSTTAIL && field[xx][yy] <= LASTTAIL)
change(xx, yy, WOOD);
break;
case GROWER:
effect(FXGET_GROWER);
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] == WOOD)
for (xxx = xx - 1; xxx <= xx + 1; xxx++)
for (yyy = yy - 1; yyy <= yy + 1; yyy++) if (valid(xxx, yyy) && field[xxx][yyy] == EMPTY)
field[xxx][yyy] = TEMPWOOD;
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] == TEMPWOOD)
change(xx, yy, WOOD);
break;
case CLOCK:
secondsperlevel -= rand() % CLOCKRAND;
if (secondsperlevel < 0)
secondsperlevel = 0;
break;
case CUTTER:
for (i = 0; i <= 3; i++)
if (worm[i].lives && worm[i].cutter)
{ worm[i].cutter = 0;
icon(i, CUTTER);
}
break;
default:
// assert(0);
break;
} }
elif (c == EMPTY)
orbscore(which, EMPTYPOINT);
elif (c == SILVER)
orbscore(which, SILVERPOINT);
elif (c == GOLD)
orbscore(which, GOLDPOINT);
} }
x = creature[which].x; /* We refresh these in case a fragment has been */
y = creature[which].y; /* reflected. Yes, it is vital. */
if (creature[which].alive && creature[which].visible && (creature[which].deltax || creature[which].deltay))
{ if (creature[which].species == MISSILE)
change(x, y, FIRSTMISSILE + creature[which].type);
elif (creature[which].species == DRIP)
change(x, y, FIRSTDRIP + creature[which].type);
elif (creature[which].species == ORB)
{ field[x][y] = ORB;
if (creature[which].mode == TONGUE)
draw(x, y, ORBTONGUE);
elif (creature[which].mode == ARMOUR)
draw(x, y, ORBARMOUR);
else draw(x, y, ORB);
} else /* fragments, goats, penguins, cyclones, dogs, clouds */
change(x, y, creature[which].species);
}
if (creature[which].alive)
{ /* decide whether to fire */
if (creature[which].species == GOAT)
{ if (!(rand() % 10))
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ creature[i].deltax = (rand() % 3) - 1;
creature[i].deltay = (rand() % 3) - 1;
if
( valid(x + creature[i].deltax, y + creature[i].deltay)
&& (creature[i].deltax || creature[i].deltay)
)
{ c = field[x + creature[i].deltax][y + creature[i].deltay];
if
( (c >= FIRSTEMPTY && c <= LASTEMPTY)
|| (c >= FIRSTTAIL && c <= LASTTAIL)
)
{ effect(FXDO_GOAT);
creature[i].alive = TRUE;
creature[i].species = FRAGMENT;
creature[i].x = x + creature[i].deltax;
creature[i].y = y + creature[i].deltay;
creature[i].visible = TRUE;
creature[i].last = EMPTY;
creature[i].speed = (SBYTE) atleast(FRAGSPEED - (level / 2), 1);
} }
break;
} } } }
elif (creature[which].species == CLOUD)
{ if (!(rand() % 30))
{ cloudbullet(which, x, y, -1);
cloudbullet(which, x, y, 1);
} }
elif (creature[which].species == OCTOPUS)
{ if (creature[which].dir == -1)
{ if (!(rand() % 20))
{ creature[which].dir = 0;
} } } } }
MODULE void cloudbullet(UBYTE which, SBYTE x, SBYTE y, SBYTE deltay)
{ UBYTE i, c;
for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ creature[i].deltax = 0;
creature[i].deltay = deltay;
if (valid(x + creature[i].deltax, y + creature[i].deltay))
{ c = field[x + creature[i].deltax][y + creature[i].deltay];
if
( (c >= FIRSTEMPTY && c <= LASTEMPTY)
|| (c >= FIRSTTAIL && c <= LASTTAIL)
)
{ effect(FXDO_CLOUD);
creature[i].alive = TRUE;
creature[i].species = FRAGMENT;
creature[i].x = x;
creature[i].y = y + creature[i].deltay;
creature[i].visible = TRUE;
creature[i].last = EMPTY;
creature[i].speed = (SBYTE) atleast(FRAGSPEED - (level / 2), 1);
} }
break;
} } }
MODULE void orbsplit(SBYTE which)
{ SBYTE copy = 0, i;
effect(FXDO_ORB);
for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ creature[i].x = creature[which].x;
creature[i].y = creature[which].y;
creature[i].score = creature[which].score;
creature[i].armour = creature[which].armour;
creature[i].tongue = creature[which].tongue;
creature[i].mode = creature[which].mode;
creature[i].speed = creature[which].speed;
creature[i].explode = FALSE;
creature[i].multi = creature[which].multi;
creature[i].last = EMPTY;
creature[i].species = ORB;
switch (copy)
{
case 0:
if (creature[which].deltax != -1 || creature[which].deltay != -1)
{ creature[i].deltax = -1;
creature[i].deltay = -1;
creature[i].alive = TRUE;
}
break;
case 1:
if (creature[which].deltax != 1 || creature[which].deltay != 1)
{ creature[i].deltax = 1;
creature[i].deltay = 1;
creature[i].alive = TRUE;
}
break;
case 2:
if (creature[which].deltax != 1 || creature[which].deltay != -1)
{ creature[i].deltax = 1;
creature[i].deltay = -1;
creature[i].alive = TRUE;
}
break;
case 3:
if (creature[which].deltax != -1 || creature[which].deltay != 1)
{ creature[i].deltax = -1;
creature[i].deltay = 1;
creature[i].alive = TRUE;
}
break;
default:
break;
}
if (++copy >= 4)
return;
} } }
SBYTE partner(SBYTE which)
{ if (which % 2 == 0)
return((SBYTE) (which + 1));
else return((SBYTE) (which - 1));
}
/* NAME putletter -- Put a letter onto the field
SYNOPSIS void putletter(SBYTE player);
INPUTS SBYTE player -
0-3: player on whose behalf the letter is put on for
-1: any letter
RESULT none */
MODULE void putletter(SBYTE player)
{ ABOOL done;
SBYTE i, x, y;
UBYTE letter;
SBYTE oldlettery = lettery;
do
{ done = findempty(&x, &y, FIRSTEMPTY, LASTEMPTY);
} while (!done);
if (player != -1)
{ for (i = 0; i <= LETTERS; i++)
if (!(letters[player][i]))
{ break;
}
if (i > LETTERS) /* if no spare letters */
letter = rand() % (LETTERS + 1);
else
{ do
letter = rand() % (LETTERS + 1);
while (letters[player][letter]);
} }
else letter = rand() % (LETTERS + 1);
change(x, y, letter + FIRSTLETTER);
letterx = x;
lettery = y;
lettertype = letter + FIRSTLETTER;
updatearrow(oldlettery);
updatearrow(lettery);
}
/* NAME queue -- adds a keystroke to the key queue
SYNOPSIS name(SBYTE, SBYTE, SBYTE);
FUNCTION Adds a keystroke to the in-game key queue.
INPUTS player - player that pressed the key
deltax - the deltax of the key
deltay - the deltay of the key
IMPLEMENTATION
thewormqueue[] array has WORMQUEUELIMIT as its last index.
It is implemented as a FIFO stack rather than LIFO so that
the keystrokes are processed in the correct order (that is,
the order in which they were pressed). The oldest keystroke
is always at index [0], the next oldest at [1], and so on
upwards to the newest keystroke, at [worm[player].pos].
Keystrokes are removed from the bottom of the array ([0]),
and the rest of the array is shuffled down to fill the gap,
so that the contents of [1] go to [0], the contents of [2]
go to [1], etc. worm[player].pos is adjusted to always point
to the newest entry, which is the 'end' of the queue.
MODULE engine.c */
void wormqueue(SBYTE player, SBYTE deltax, SBYTE deltay)
{ if (worm[player].pos < WORMQUEUELIMIT)
{ worm[player].pos++;
thewormqueue[player][worm[player].pos].deltax = deltax;
thewormqueue[player][worm[player].pos].deltay = deltay;
} }
MODULE void dogqueue(SBYTE which, SBYTE deltax, SBYTE deltay)
{ if (creature[which].pos < DOGQUEUELIMIT)
{ creature[which].pos++;
thedogqueue[which][creature[which].pos].deltax = deltax;
thedogqueue[which][creature[which].pos].deltay = deltay;
} else
{ creature[which].alive = FALSE;
change(creature[which].x, creature[which].y, EMPTY);
} }
MODULE void reflect(UBYTE which)
{ creature[which].deltax = -creature[which].deltax;
creature[which].deltay = -creature[which].deltay;
creature[which].x += creature[which].deltax * 2;
creature[which].y += creature[which].deltay * 2;
}
ABOOL savefields(STRPTR fieldname)
{ SBYTE i, j;
TEXT IOBuffer[NAMELENGTH + 1];
matchteleports();
if (!ZOpen(fieldname, TRUE))
return FALSE;
/* write header */
strcpy(IOBuffer, "FSET 6.6");
IOBuffer[9] = levels;
if (!ZWrite(IOBuffer, 10))
{ ZClose();
return FALSE;
}
/* write high score table */
for (i = 0; i <= HISCORES; i++)
{ IOBuffer[0] = hiscore[i].player;
IOBuffer[1] = hiscore[i].level;
IOBuffer[2] = hiscore[i].score / 16777216;
IOBuffer[3] = (hiscore[i].score % 16777216) / 65536;
IOBuffer[4] = ((hiscore[i].score % 16777216) % 65536) / 256;
IOBuffer[5] = ((hiscore[i].score % 16777216) % 65536) % 256;
if (!ZWrite(IOBuffer, 6))
{ ZClose();
return FALSE;
}
for (j = 0; j <= NAMELENGTH; j++)
IOBuffer[j] = hiscore[i].name[j];
if (!ZWrite(IOBuffer, NAMELENGTH + 1))
{ ZClose();
return FALSE;
}
for (j = 0; j <= TIMELENGTH; j++)
IOBuffer[j] = hiscore[i].time[j];
if (!ZWrite(IOBuffer, TIMELENGTH + 1))
{ ZClose();
return FALSE;
}
for (j = 0; j <= DATELENGTH; j++)
IOBuffer[j] = hiscore[i].date[j];
if (!ZWrite(IOBuffer, DATELENGTH + 1))
{ ZClose();
return FALSE;
} }
/* write level data */
for (i = 0; i <= levels; i++)
{ IOBuffer[0] = startx[i];
IOBuffer[1] = starty[i];
IOBuffer[2] = teleport[i][0].alive;
IOBuffer[3] = teleport[i][0].x;
IOBuffer[4] = teleport[i][0].y;
IOBuffer[5] = teleport[i][1].alive;
IOBuffer[6] = teleport[i][1].x;
IOBuffer[7] = teleport[i][1].y;
if (!ZWrite(IOBuffer, 8))
{ ZClose();
return FALSE;
}
if (!ZWrite((char *) &board[i][0][0], LEVELSIZE))
{ ZClose();
return FALSE;
} }
/* write version string */
if (!ZWrite(VERSION, strlen(VERSION)))
{ ZClose();
return FALSE;
}
ZClose();
if (clearthem)
clearhiscores();
modified = FALSE;
return TRUE;
}
void saylevel(COLOUR colour)
{ TEXT mainstring[15] = "Level ", tempstring[4];
if (level > 0)
{ stci_d(&mainstring[6], level);
strcat((char*) mainstring, " of ");
stci_d(tempstring, levels);
strcat((char*) mainstring, (char*) tempstring);
say(mainstring, colour);
} else
{ if (a == FIELDEDIT)
say("Bonus Level", colour);
else
{ if (leveltype == TREASURE)
say("Bonus Level: Treasury!", colour);
elif (leveltype == DRIP)
say("Bonus Level: Drips!", colour);
else
{ // assert(leveltype == PENGUIN);
say("Bonus Level: Penguins!", colour);
} } } }
MODULE SBYTE slowdown(SBYTE speed, ABOOL nitro)
{ speed *= 2;
if (nitro)
{ if (speed > VERYSLOW)
speed = VERYSLOW;
} elif (speed > SLOW)
speed = SLOW;
return(speed);
}
MODULE void slowloop(void)
{ SBYTE i, player, which, x, xx, y, yy;
UBYTE thissy;
/* decrement worm strength */
for (player = 0; player <= 3; player++)
if (worm[player].lives > 0 && (ice == -1 || ice == player))
{ if (worm[player].bias > 0)
{ worm[player].bias--;
stat(player, BIAS);
}
if (worm[player].cutter > 0)
{ worm[player].cutter--;
icon(player, CUTTER);
}
if (worm[player].freedom > 0)
{ worm[player].freedom--;
icon(player, FREEDOM);
}
if (worm[player].ice > 0)
{ worm[player].ice--;
icon(player, ICE);
for (which = 0; which <= 3; which++)
if (player != which)
worm[which].pos = -1;
ice = -1;
icon(player, ICE);
}
if (worm[player].mode == ARMOUR)
{ if (--worm[player].armour == 0)
{ if (worm[player].tongue > 0)
worm[player].mode = TONGUE;
else worm[player].mode = NOMODE;
}
stat(player, ARMOUR);
} elif (worm[player].mode == TONGUE)
{ if (--worm[player].tongue == 0)
{ if (worm[player].armour > 0)
worm[player].mode = ARMOUR;
else worm[player].mode = NOMODE;
}
stat(player, TONGUE);
} }
/* blank out old causes */
for (player = 0; player <= 3; player++)
{ if (worm[player].lives > 0 && r > worm[player].causewait)
{ drawcause(player, BLACK);
worm[player].causewait = (ULONG) -1; /* most future time possible */
} }
if (ice == -1)
{ if (level)
{ /* decrement orb strength */
for (which = 0; which <= CREATURES; which++)
if (creature[which].alive && creature[which].species == ORB)
if (creature[which].mode == ARMOUR)
{ if (--creature[which].armour == 0)
if (creature[which].tongue > 0)
creature[which].mode = TONGUE;
else creature[which].mode = NOMODE;
} elif (creature[which].mode == TONGUE)
{ if (--creature[which].tongue == 0)
if (creature[which].armour > 0)
creature[which].mode = ARMOUR;
else creature[which].mode = NOMODE;
}
/* create goats */
if
( (!(rand() % freq))
&& findempty(&x, &y, FIRSTGOAT, LASTGOAT)
&& field[x][y] != GOAT
)
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ effect(FXBORN_GOAT);
creature[i].x = x;
creature[i].y = y;
creature[i].alive = TRUE;
creature[i].species = GOAT;
creature[i].last = creature[i].oldlast = field[x][y];
creature[i].speed = (SBYTE) atleast(GOATSPEED - (level / 2), 2);
creature[i].visible = TRUE;
change(x, y, GOAT);
break;
} } }
/* create octopi */
if
( (!(rand() % (freq * 2)))
&& findempty(&x, &y, FIRSTGOAT, LASTGOAT)
&& field[x][y] != GOAT
)
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ creature[i].x = x;
creature[i].y = y;
creature[i].alive = TRUE;
creature[i].species = OCTOPUS;
creature[i].visible = TRUE;
creature[i].speed = VERYSLOW;
creature[i].dir = -1;
creature[i].deltax = creature[i].deltay = 0;
change(x, y, OCTOPUS);
break;
} } }
/* create orbs */
if
( (!(rand() % freq))
&& findempty(&x, &y, FIRSTEMPTY, LASTEMPTY)
)
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ effect(FXBORN_ORB);
creature[i].deltax = (rand() % 2) * 2 - 1;
creature[i].deltay = (rand() % 2) * 2 - 1;
creature[i].score = 0;
creature[i].alive = TRUE;
creature[i].armour = 0;
creature[i].tongue = 0;
creature[i].mode = NOMODE;
creature[i].speed = (SBYTE) atleast(ORBSPEED - (level / 2), 2);
creature[i].species = ORB;
creature[i].explode = FALSE;
creature[i].multi = 1;
creature[i].x = x;
creature[i].y = y;
creature[i].last = EMPTY;
creature[i].visible = TRUE;
change(x, y, ORB);
break;
} } }
/* create dogs */
if ((!(rand() % freq)) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
{ for (i = 0; i <= CREATURES; i++)
if (!(creature[i].alive))
{ creature[i].x = x;
creature[i].y = y;
creature[i].last = EMPTY;
creature[i].speed = (SBYTE) atleast(DOGSPEED - (level / 2), 2);
creature[i].visible = TRUE;
creature[i].deltax = 0;
creature[i].deltay = 0;
creature[i].species = DOG;
creature[i].alive = TRUE;
creature[i].pos = -1;
creature[i].dormant = DORMANT; /* dormant */
field[x][y] = DOG;
draw(x, y, DOGDORMANT);
break;
} }
/* create slime */
if ((!(rand() % (freq * 2))) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
change(x, y, SLIME);
/* grow slime */
if (!(rand() % (freq * 2)))
{ for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == SLIME)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy) && field[xx][yy] == EMPTY && !(rand() % 2))
field[xx][yy] = TEMPSLIME;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == TEMPSLIME)
change(x, y, SLIME);
}
/* create timebombs */
if ((!(rand() % (freq * 8))) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
{ for (i = 0; i <= CREATURES; i++)
if (!(creature[i].alive))
{ creature[i].x = x;
creature[i].y = y;
creature[i].last = EMPTY;
creature[i].speed = (SBYTE) atleast(TIMEBOMBSPEED - (level / 2), 2);
creature[i].visible = TRUE;
creature[i].deltax = 0;
creature[i].deltay = 0;
creature[i].species = TIMEBOMB;
creature[i].alive = TRUE;
creature[i].time = 10;
field[x][y] = TIMEBOMB;
draw(x, y, ZERO + 9);
break;
} } }
/* create drips */
if
( (level && (!(rand() % freq)))
|| ((!level) && leveltype == DRIP && (!(rand() % FREQ_DRIP)))
)
{ x = (rand() % (FIELDX + 1));
y = (rand() % 3);
thissy = field[x][y];
if (thissy >= FIRSTEMPTY && thissy <= LASTEMPTY)
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ effect(FXBORN_DRIP);
creature[i].alive = TRUE;
creature[i].last = EMPTY;
creature[i].species = DRIP;
creature[i].type = rand() % 4;
creature[i].x = x;
creature[i].y = y;
creature[i].deltax = 0;
creature[i].deltay = 1;
creature[i].visible = TRUE;
if (level)
creature[i].speed = (SBYTE) atleast(DRIPSPEED - (level / 2), 2);
else creature[i].speed = BONUSDRIPSPEED;
change(x, y, FIRSTDRIP + creature[i].type);
break;
} } } }
/* create penguins */
if
(( ( level && !(rand() % freq))
|| ((!level) && leveltype == PENGUIN && (!(rand() % FREQ_PENGUIN)))
) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY)
)
{ for (i = 0; i <= CREATURES; i++)
if (!(creature[i].alive))
{ effect(FXBORN_PENGUIN);
creature[i].x = x;
creature[i].y = y;
creature[i].last = EMPTY;
if (level)
creature[i].speed = (SBYTE) atleast(PENGUINSPEED - (level / 2), 2);
else creature[i].speed = BONUSPENGUINSPEED;
creature[i].visible = TRUE;
creature[i].deltax = 0;
creature[i].deltay = 0;
creature[i].species = PENGUIN;
creature[i].alive = TRUE;
change(x, y, PENGUIN);
break;
} }
/* create clouds */
if (level && (!(rand() % freq)))
{ x = (rand() % (FIELDX - 1)) + 1;
y = (rand() % (FIELDY - 1)) + 1;
/* If you create clouds at an extreme horizontal position
(0 or FIELDX), will cause a crash because the deltas will be
wrong. */
thissy = field[x][y];
if (thissy >= FIRSTEMPTY && thissy <= LASTEMPTY)
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ creature[i].alive = TRUE;
creature[i].last = EMPTY;
creature[i].species = CLOUD;
creature[i].x = x;
creature[i].y = y;
if (x < FIELDX / 2)
creature[i].deltax = 1;
else creature[i].deltax = -1;
creature[i].deltay = 0;
creature[i].visible = TRUE;
creature[i].speed = (SBYTE) atleast(CLOUDSPEED - (level / 2), 3);
change(x, y, CLOUD);
break;
} } } }
/* create objects */
for (which = 0; which <= LASTOBJECT; which++)
if (level || leveltype != TREASURE || which == TREASURE)
{ if (!(rand() % object[which].freq) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
change(x, y, which);
} elif (!(rand() % (object[which].freq / 10)) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
change(x, y, which);
/* create teleports */
if (!(rand() % FREQ_TELEPORT)
&& !teleport[level][2].alive
&& findempty(&(teleport[level][2].x), &(teleport[level][2].y), FIRSTEMPTY, LASTEMPTY)
&& findempty(&(teleport[level][3].x), &(teleport[level][3].y), FIRSTEMPTY, LASTEMPTY)
&& (teleport[level][2].x != teleport[level][3].x || teleport[level][2].y != teleport[level][3].y))
{ teleport[level][2].alive = TRUE;
teleport[level][3].alive = TRUE;
change(teleport[level][2].x, teleport[level][2].y, TELEPORT);
change(teleport[level][3].x, teleport[level][3].y, TELEPORT);
} }
}
MODULE SBYTE speedup(SBYTE speed, ABOOL nitro)
{ speed /= 2;
if (speed < FAST)
speed = FAST;
return(speed);
}
MODULE ULONG squareblast(SBYTE type, SBYTE player, SBYTE thissy, SBYTE x, SBYTE y, ABOOL cutter)
{ SBYTE which;
ULONG score = 0;
SBYTE filler;
if (type == HEAD && worm[player].bias)
filler = SILVER;
else filler = EMPTY;
if (thissy <= LASTOBJECT)
{ if (!cutter)
{ change(x, y, filler);
} }
elif (thissy >= FIRSTTAIL && thissy <= LASTTAIL)
{ change(x, y, filler);
} elif (thissy == ORB)
{ which = whichcreature(x, y, ORB, 255);
if (creature[which].mode != ARMOUR)
{ effect(FXDEATH_ORB);
creature[which].alive = FALSE;
score = creature[which].score;
if (type == HEAD && worm[player].bias)
worm[player].lives += ORBBLOOD;
change(x, y, BONUS);
} else effect(FXUSE_ARMOUR);
} elif (thissy >= FIRSTDRIP && thissy <= LASTDRIP)
{ creature[whichcreature(x, y, DRIP, 255)].alive = FALSE;
change(x, y, filler);
} elif (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (type != HEAD || player != thissy - FIRSTHEAD)
if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = BOMB;
worm[thissy - FIRSTHEAD].alive = FALSE;
stat(thissy - FIRSTHEAD, LIFE);
if (type == HEAD)
worm[thissy - FIRSTHEAD].victor = player;
else
{ worm[thissy - FIRSTHEAD].victor = -1;
score = KILLWORM; // worms will get this bonus from death(), so it is not given for them here.
} }
else effect(FXUSE_ARMOUR);
} elif (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ if (type != HEAD || player != thissy - FIRSTMISSILE)
{ creature[whichcreature(x, y, MISSILE, 255)].alive = FALSE;
change(x, y, filler);
} }
elif (thissy == PENGUIN)
{ creature[whichcreature(x, y, PENGUIN, 255)].alive = FALSE;
change(x, y, filler);
} elif (thissy == FRAGMENT)
{ creature[whichcreature(x, y, FRAGMENT, 255)].alive = FALSE;
change(x, y, filler);
} elif (thissy == GOAT)
{ creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
change(x, y, BONUS);
}
return(score);
}
void timeloop(void)
{ TEXT timedisplay[5] = {"#:##"};
PERSIST ABOOL outoftime = FALSE;
UBYTE i;
secondsleft = atleast(secondsleft, 0);
timedisplay[0] = 48 + (secondsleft / 60);
timedisplay[2] = 48 + ((secondsleft % 60) / 10);
timedisplay[3] = 48 + ((secondsleft % 60) % 10);
if (!level)
{ say(timedisplay, worm[treasurer].colour);
if (!secondsleft)
{ level = reallevel + 1;
secondsleft = SECONDSPERLEVEL;
newlevel(treasurer);
} }
elif (!secondsleft)
{ if (!outoftime)
{ effect(FXSIREN);
say("Out of time!", WHITE);
outoftime = TRUE;
freq /= 2;
for (i = 0; i <= CREATURES; i++)
if (creature[i].alive)
creature[i].speed = speedup(creature[i].speed, TRUE);
} }
else
{ outoftime = FALSE;
say(timedisplay, WHITE);
} }
void train(SCANCODE scancode)
{ SBYTE i, x, y;
switch(scancode)
{
case HELP:
trainer = !trainer;
break;
case NUMERICSLASH:
/* Complete the level. */
if (trainer)
{ trainer = FALSE;
if (worm[1].lives > 0)
for (i = 0; i <= LETTERS; i++)
{ letters[1][i] = TRUE;
drawletter(1, FIRSTLETTER + i, NORMAL);
} }
break;
case NUMERICASTERISK:
/* field[][] dump, for debugging purposes. */
if (trainer)
{ trainer = FALSE;
say("Field dump...", PURPLE);
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
draw(x, y, field[x][y]);
anykey(FALSE);
}
break;
case NUMERICPLUS:
if (trainer)
{ trainer = FALSE;
say("Trainer activated!", PURPLE);
anykey(FALSE);
if (worm[1].lives > 0)
{ worm[1].lives = LIVESLIMIT;
stat(1, LIFE);
worm[1].tongue = MODELIMIT;
worm[1].mode = TONGUE;
stat(1, TONGUE);
worm[1].bias = BIASLIMIT;
stat(1, BIAS);
worm[1].ammo = AMMOLIMIT;
stat(1, AMMO);
worm[1].power = POWERLIMIT;
stat(1, POWER);
worm[1].nitro = TRUE;
stat(1, NITRO);
worm[1].affixer = TRUE;
icon(1, AFFIXER);
worm[1].remnants = TRUE;
icon(1, REMNANTS);
worm[1].sideshot = TRUE;
icon(1, SIDESHOT);
worm[1].pusher = TRUE;
icon(1, PUSHER);
worm[1].cutter = 100;
icon(1, CUTTER);
trainer = FALSE;
} }
break;
default:
break;
} }
MODULE void turnworm(SBYTE player, SBYTE deltax, SBYTE deltay)
{
if (worm[player].nitro || !deltax || !deltay)
{ if (worm[player].deltax == deltax && worm[player].deltay == deltay)
{ worm[player].speed = speedup(worm[player].speed, worm[player].nitro);
stat(player, NITRO);
} elif (worm[player].deltax == -deltax && worm[player].deltay == -deltay)
{ worm[player].speed = slowdown(worm[player].speed, worm[player].nitro);
stat(player, NITRO);
} else
{ worm[player].deltax = deltax;
worm[player].deltay = deltay;
} }
}
SBYTE valid(SBYTE x, SBYTE y)
{ if (x >= 0 && x <= FIELDX && y >= 0 && y <= FIELDY)
return(TRUE);
else return(FALSE);
}
MODULE UBYTE whichcreature(SBYTE x, SBYTE y, UBYTE species, UBYTE exception)
{ UBYTE i;
for (i = 0; i <= CREATURES; i++)
if
( creature[i].alive
&& creature[i].x == x
&& creature[i].y == y
&& creature[i].species == species
&& i != exception
)
return i;
return 255; /* error code */
}
MODULE SBYTE whichteleport(SBYTE x, SBYTE y)
{ SBYTE which;
for (which = 0; which <= 3; which++)
if (teleport[level][which].alive && teleport[level][which].x == x && teleport[level][which].y == y)
return((SBYTE) which);
return((SBYTE) -1); /* error code */
}
MODULE void wormbullet(SBYTE player)
{ ABOOL finished,
flag = FALSE,
ok = FALSE,
lettered = FALSE;
LONG score;
SBYTE distance,
thissy,
i, j,
x, y;
if (!worm[player].ammo)
{ stat(player, BONUS);
if (worm[player].speed == FAST)
distance = FASTDISTANCE;
elif (worm[player].speed == NORMAL)
distance = NORMALDISTANCE;
elif (worm[player].speed == SLOW)
distance = SLOWDISTANCE;
else
{ // assert(worm[player].speed == VERYSLOW);
distance = VERYSLOWDISTANCE;
}
/* check for metal */
for (i = 1; i < distance; i++)
{ x = xwrap(worm[player].x + (i * worm[player].deltax));
y = ywrap(worm[player].y + (i * worm[player].deltay));
if (field[x][y] == METAL)
flag = TRUE;
}
if (!flag)
{ // assert(abs(worm[player].deltax) <= 1 && abs(worm[player].deltay) <= 1);
x = xwrap(worm[player].x + (worm[player].deltax * distance));
y = ywrap(worm[player].y + (worm[player].deltay * distance));
thissy = field[x][y];
if (thissy == TELEPORT)
{ i = whichteleport(x, y);
if (!blocked(i, worm[player].deltax, worm[player].deltay))
ok = TRUE;
}
if (ok || ((thissy < STONE || thissy > GOAT) && thissy != METAL))
{ effect(FXDO_JUMP);
worm[player].deltax *= distance;
worm[player].deltay *= distance;
} } }
else
{ effect(FXUSE_AMMO);
worm[player].ammo--;
stat(player, AMMO);
if (worm[player].bias)
createmissile(player, worm[player].x, worm[player].y);
if (worm[player].sideshot)
{ bullet[7].alive = bullet[8].alive = TRUE;
bullet[7].teleported = bullet[8].teleported = 0;
bullet[7].visible = bullet[8].visible = TRUE;
bullet[7].reflected = bullet[8].reflected = FALSE;
bullet[7].x = bullet[8].x = worm[player].x;
bullet[7].y = bullet[8].y = worm[player].y;
if (!worm[player].deltax && worm[player].deltay)
{ bullet[7].deltax = -1;
bullet[8].deltax = 1;
bullet[7].deltay = bullet[8].deltay = 0;
} elif (worm[player].deltax && !worm[player].deltay)
{ bullet[7].deltax = bullet[8].deltax = 0;
bullet[7].deltay = -1;
bullet[8].deltay = 1;
} else /* worm is diagonal */
{ if (worm[player].deltax == worm[player].deltay)
{ bullet[7].deltax = 1;
bullet[7].deltay = -1;
} else
{ bullet[7].deltax = -1;
bullet[7].deltay = -1;
}
bullet[8].deltax = -bullet[7].deltax;
bullet[8].deltay = -bullet[7].deltay;
} }
for (i = 0; i <= worm[player].power; i++)
{ bullet[i].alive = TRUE;
bullet[i].teleported = 0;
bullet[i].visible = TRUE;
bullet[i].reflected = FALSE;
bullet[i].deltax = worm[player].deltax;
bullet[i].deltay = worm[player].deltay;
if (i % 2 == 0)
distance = i / 2;
else distance = -((i + 1) / 2);
if (worm[player].deltax == 0)
{ bullet[i].x = worm[player].x + distance;
bullet[i].y = worm[player].y;
} elif (worm[player].deltay == 0)
{ bullet[i].x = worm[player].x;
bullet[i].y = worm[player].y + distance;
} else
{ switch (i)
{
case 0:
bullet[i].x = worm[player].x + worm[player].deltax;
bullet[i].y = worm[player].y + worm[player].deltay;
break;
case 1:
bullet[i].x = worm[player].x + worm[player].deltax;
bullet[i].y = worm[player].y;
break;
case 2:
bullet[i].x = worm[player].x;
bullet[i].y = worm[player].y + worm[player].deltay;
break;
case 3:
bullet[i].x = worm[player].x + worm[player].deltax * 2;
bullet[i].y = worm[player].y;
break;
case 4:
bullet[i].x = worm[player].x;
bullet[i].y = worm[player].y + worm[player].deltay * 2;
break;
case 5:
bullet[i].x = worm[player].x + worm[player].deltax * 2;
bullet[i].y = worm[player].y - worm[player].deltay;
break;
case 6:
bullet[i].x = worm[player].x - worm[player].deltax;
bullet[i].y = worm[player].y + worm[player].deltay * 2;
break;
default:
break;
} } }
/* Bullets are now set up. */
score = 0;
finished = FALSE;
while (!finished)
{ finished = TRUE;
for (i = 0; i <= 8; i++)
{ if (bullet[i].alive)
{ finished = FALSE;
if (bullet[i].reflected)
{ bullet[i].x -= bullet[i].deltax;
bullet[i].y -= bullet[i].deltay;
} else
{ bullet[i].x += bullet[i].deltax;
bullet[i].y += bullet[i].deltay;
}
x = bullet[i].x;
y = bullet[i].y;
thissy = field[x][y];
if (!(valid(x, y)))
bullet[i].alive = FALSE;
elif (x == worm[player].x && y == worm[player].y)
{ /* hit by own bullet */
bullet[i].alive = FALSE;
if (worm[player].mode != ARMOUR)
{ worm[player].cause = FIRSTFIRE + player;
worm[player].victor = -1;
worm[player].alive = FALSE;
} }
elif (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = FIRSTFIRE + player;
worm[thissy - FIRSTHEAD].victor = player;
worm[thissy - FIRSTHEAD].alive = FALSE;
if (player != thissy - FIRSTHEAD)
score += KILLWORM + HITSHOT;
} else effect(FXUSE_ARMOUR);
bullet[i].alive = FALSE;
} elif (thissy >= FIRSTPROTECTOR && thissy <= LASTPROTECTOR)
{ if (player != thissy - FIRSTPROTECTOR)
{ effect(FXUSE_PROTECTOR);
bullet[i].alive = FALSE;
} else bullet[i].visible = FALSE;
} elif (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ if (player != thissy - FIRSTMISSILE)
creature[whichcreature(x, y, MISSILE, 255)].alive = FALSE;
else bullet[i].visible = FALSE;
} elif (thissy >= FIRSTDRIP && thissy <= LASTDRIP)
{ j = whichcreature(x, y, DRIP, 255);
creature[j].alive = FALSE;
} elif (thissy >= FIRSTLETTER && thissy <= LASTLETTER)
{ wormletter(player, thissy);
lettered = TRUE;
} else
{ switch(thissy)
{
case METAL:
if (bullet[i].reflected)
bullet[i].alive = FALSE;
else
{ bullet[i].reflected = TRUE;
bullet[i].x -= bullet[i].deltax * 2;
bullet[i].y -= bullet[i].deltay * 2;
}
break;
case STONE:
case WHIRLWIND:
bullet[i].alive = FALSE;
break;
case ORB:
bullet[i].alive = FALSE;
j = whichcreature(x, y, ORB, 255);
if (creature[j].mode != ARMOUR)
{ creature[j].explode = TRUE;
score += creature[j].score + HITSHOT;
if (worm[player].bias)
worm[player].lives += ORBBLOOD;
} else effect(FXUSE_ARMOUR);
break;
case TELEPORT:
j = whichteleport(bullet[i].x, bullet[i].y);
if (bullet[i].teleported == 2)
bullet[i].alive = FALSE;
else
{ effect(FXUSE_TELEPORT);
bullet[i].visible = FALSE;
bullet[i].teleported++;
bullet[i].x = teleport[level][partner(j)].x;
bullet[i].y = teleport[level][partner(j)].y;
score += TELPOINT;
}
break;
case WOOD:
if (!worm[player].bias)
bullet[i].alive = FALSE;
break;
case OCTOPUS:
creature[whichcreature(x, y, OCTOPUS, 255)].alive = FALSE;
change(x, y, BONUS);
score += KILLOCTOPUS + HITSHOT;
if (worm[player].bias)
{ worm[player].lives += OCTOPUSBLOOD;
}
bullet[i].alive = FALSE;
break;
case GOAT:
effect(FXDEATH_GOAT);
creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
change(x, y, BONUS);
score += KILLGOAT + HITSHOT;
if (worm[player].bias)
{ worm[player].lives += GOATBLOOD;
}
bullet[i].alive = FALSE;
break;
case SKULL:
bullet[i].alive = FALSE;
break;
case FRAGMENT:
bullet[i].alive = FALSE;
creature[whichcreature(x, y, FRAGMENT, 255)].alive = FALSE;
change(x, y, EMPTY);
break;
case PENGUIN:
bullet[i].alive = FALSE;
creature[whichcreature(x, y, PENGUIN, 255)].alive = FALSE;
change(x, y, EMPTY);
score += KILLPENGUIN + HITSHOT;
if (worm[player].bias)
worm[player].lives += PENGUINBLOOD;
break;
case DOG:
bullet[i].alive = FALSE;
break;
case CLOUD:
creature[whichcreature(x, y, CLOUD, 255)].alive = FALSE;
change(x, y, EMPTY);
break;
case TIMEBOMB:
creature[whichcreature(x, y, TIMEBOMB, 255)].alive = FALSE;
bombblast(BOMB, 0, x, y);
change(x, y, EMPTY);
break;
default:
break;
} }
// x and y need this refreshing here
x = bullet[i].x;
y = bullet[i].y;
if (bullet[i].alive && bullet[i].visible)
{ draw(x, y, FIRSTFIRE + player);
if (worm[player].remnants)
field[x][y] = FIRSTFIRE + player;
elif (bullet[i].teleported)
change(x, y, GOLD);
else change(x, y, EMPTY);
} } } }
if (lettered)
putletter(player);
wormscore(player, score);
if (worm[player].bias)
stat(player, LIFE);
clearkybd();
} }
MODULE void wormletter(SBYTE player, SBYTE c)
{ switch(c)
{
case GREEN_C:
effect(FX_C);
break;
case RED_O:
effect(FX_O);
break;
case BLUE_M:
effect(FX_M);
break;
case YELLOW_P:
effect(FX_P);
break;
case GREEN_L:
effect(FX_L);
break;
case BLUE_T:
effect(FX_T);
break;
default:
// assert(c == RED_E || c == YELLOW_E);
effect(FX_E);
break;
}
letters[player][c - FIRSTLETTER] = TRUE;
drawletter(player, c, NORMAL);
wormscore(player, LETTERPOINT);
}
/* NAME wormloop -- controls worms and protectors
SYNOPSIS wormloop(SBYTE);
FUNCTION Amiga-worm thinking, processing one keystroke from the
worm's queue, all the worm's protectors' control, the
worm's control.
MODULE engine.c */
MODULE void wormloop(SBYTE player)
{ AUTO ABOOL enclosed = FALSE, ok;
AUTO SBYTE bestx = 0, besty = 0, // to avoid spurious warnings
c, d, dirx, diry, i, k, thisprot, x, y,
leftx, rightx, topy, bottomy;
AUTO SWORD bestgood, good;
AUTO UBYTE interior;
AUTO ULONG score = 0;
PERSIST struct
{ SBYTE deltax[4], deltay[4];
} deltas[4] =
{ { { 0, -1, 0, 1}, // northwest
{-1, 0, 1, 0}
},
{ { 0, 1, 0, -1}, // northeast
{-1, 0, 1, 0}
},
{ { 0, 1, 0, -1}, // southeast
{ 1, 0, -1, 0}
},
{ { 0, -1, 0, 1}, // southwest
{ 1, 0, -1, 0}
} };
PERSIST struct
{ SBYTE leftx, rightx, topy, bottomy;
} enclose[4] =
{ {-127, -1, -127, -1 }, // northwest
{ 1, 127, -127, -1 }, // northeast
{ 1, 127, 1, 127 }, // southeast
{-127, -1, 1, 127 } // southwest
};
/* Amiga worm control
Remove a keystroke from the worm queue
Move worm (and add a keystroke to the dog queue)
Check for enclosure
Move protectors
Collision detection
AI: Amiga worm control.
Worm checks ahead, left and right one square. Assigns opinions
to those three choices and then takes the appropriate one. */
if (worm[player].control == AMIGA)
{ if (!(rand() % 50))
wormqueue(player, (rand() % 3) - 1, (rand() % 3) - 1);
else
{ bestgood = -128;
for (i = 0; i <= 2; i++)
{ switch(i)
{
case 0:
dirx = worm[player].deltax;
diry = worm[player].deltay;
break;
case 1:
if (worm[player].deltax == 0) /* if going north or south */
{ dirx = -1; /* then look west */
diry = 0;
} else /* if going east or west */
{ dirx = 0; /* then look north */
diry = -1;
}
break;
case 2:
if (worm[player].deltax == 0) /* if going north or south */
{ dirx = 1; /* then look east */
diry = 0;
} else /* if going east or west */
{ dirx = 0; /* then look south */
diry = 1;
}
break;
default:
dirx = diry = 0; // to prevent spurious warnings
break;
}
c = field[xwrap(worm[player].x + dirx)][ywrap(worm[player].y + diry)];
if (c >= FIRSTLETTER && c <= LASTLETTER)
good = LETTERPOINT;
elif (c >= FIRSTHEAD && c <= LASTHEAD)
good = -55;
elif (c <= LASTOBJECT)
good = (SWORD) object[c].score;
elif (c == FIRSTPROTECTOR + player)
good = 6;
elif (c == FIRSTFIRE && c <= LASTFIRE)
{ if (player == c - FIRSTFIRE)
good = -1;
else good = -70;
} elif (c >= FIRSTTAIL && c <= LASTTAIL)
{ if (worm[player].mode == TONGUE && worm[player].tongue > 10)
if (player != c - FIRSTTAIL)
good = 10;
else good = -10;
elif (player == c - FIRSTTAIL)
good = -30;
else good = -60;
} else switch(c)
{
case SKULL:
good = 70;
break;
case GOLD:
good = GOLDPOINT;
break;
case SILVER:
good = SILVERPOINT;
break;
case EMPTY:
good = EMPTYPOINT;
break;
case SLIME:
if (worm[player].mode == ARMOUR)
good = -2;
else good = -75;
break;
case WOOD:
if (worm[player].mode == ARMOUR)
good = -2;
else good = -70;
break;
case STONE:
good = -85;
break;
case METAL:
good = -90;
break;
case GOAT:
good = -95;
break;
case WHIRLWIND:
good = -100;
break;
case ORB:
good = -45;
break;
case CLOUD:
good = -40;
break;
default:
good = -50;
break;
}
if (good > bestgood)
{ bestx = dirx;
besty = diry;
bestgood = good;
} }
if (bestgood <= -80)
{ // turn to the right
if (worm[player].deltax == 0) /* if going north or south */
{ bestx = 1; /* then turn east */
besty = 0;
} else /* if going east or west */
{ bestx = 0; /* then turn south */
besty = 1;
} }
if (bestgood <= -60 && (!(rand() % 5)))
wormqueue(player, 0, 0);
elif (bestgood < 0 && (!(rand() % 2)))
wormqueue(player, 0, 0);
elif (bestx != worm[player].deltax || besty != worm[player].deltay)
wormqueue(player, bestx, besty);
} }
/* remove a keystroke from the worm queue */
if (worm[player].pos != -1)
{ if (thewormqueue[player][0].deltax == 0 && thewormqueue[player][0].deltay == 0)
wormbullet(player);
else turnworm(player, thewormqueue[player][0].deltax, thewormqueue[player][0].deltay);
if (--worm[player].pos != -1)
{ for (i = 0; i <= worm[player].pos; i++)
{ thewormqueue[player][i].deltax = thewormqueue[player][i + 1].deltax;
thewormqueue[player][i].deltay = thewormqueue[player][i + 1].deltay;
} } }
/* move worm */
change(worm[player].x, worm[player].y, worm[player].last);
worm[player].x = xwrap(worm[player].x + worm[player].deltax);
worm[player].y = ywrap(worm[player].y + worm[player].deltay);
if (worm[player].freedom)
worm[player].last = FIRSTFIRE + player;
else worm[player].last = FIRSTTAIL + player;
for (i = 0; i <= CREATURES; i++)
{ if (creature[i].alive && creature[i].species == DOG && creature[i].dormant > DORMANT && creature[i].type == player)
{ if (!worm[player].rammed)
dogqueue(i, worm[player].deltax, worm[player].deltay);
if (creature[i].dormant < CHASING)
{ creature[i].dormant++;
draw(creature[i].x, creature[i].y, DOGAWAKENING);
} } }
/* The deltas are not changed back to the range of -1..1 until after
the dogs have looked at the queue. This enables them to jump properly. */
worm[player].rammed = FALSE;
worm[player].deltax = bsign(worm[player].deltax);
worm[player].deltay = bsign(worm[player].deltay);
/* check for enclosure
#####
#...#
#...# . = interior
#...# # = tail
####! ! = head */
for (interior = 2; interior <= 7; interior++) // for each size of interior
{ for (i = 0; i <= 3; i++) // four times, once for each direction
{ x = worm[player].x;
y = worm[player].y;
ok = TRUE;
for (k = 0; k <= interior; k++)
{ x += deltas[i].deltax[0];
y += deltas[i].deltay[0];
if ((!valid(x, y)) || field[x][y] != FIRSTTAIL + player)
{ ok = FALSE;
break;
} }
for (k = 0; k <= interior; k++)
{ x += deltas[i].deltax[1];
y += deltas[i].deltay[1];
if ((!valid(x, y)) || field[x][y] != FIRSTTAIL + player)
{ ok = FALSE;
break;
} }
for (k = 0; k <= interior; k++)
{ x += deltas[i].deltax[2];
y += deltas[i].deltay[2];
if ((!valid(x, y)) || field[x][y] != FIRSTTAIL + player)
{ ok = FALSE;
break;
} }
// now the last side
for (k = 0; k <= interior - 1; k++)
{ x += deltas[i].deltax[3];
y += deltas[i].deltay[3];
if ((!valid(x, y)) || field[x][y] != FIRSTTAIL + player)
{ ok = FALSE;
break;
} }
if (ok)
{ effect(FXDO_ENCLOSE);
enclosed = TRUE;
if (enclose[i].leftx < -1)
enclose[i].leftx = -interior;
elif (enclose[i].leftx > 1)
enclose[i].leftx = interior + 1;
if (enclose[i].rightx < -1)
enclose[i].rightx = -(interior + 1);
elif (enclose[i].rightx > 1)
enclose[i].rightx = interior;
if (enclose[i].topy < -1)
enclose[i].topy = -interior;
elif (enclose[i].topy > 1)
enclose[i].topy = interior + 1;
if (enclose[i].bottomy < -1)
enclose[i].bottomy = -(interior + 1);
elif (enclose[i].bottomy > 1)
enclose[i].bottomy = interior;
leftx = worm[player].x + enclose[i].leftx;
rightx = worm[player].x + enclose[i].rightx;
topy = worm[player].y + enclose[i].topy;
bottomy = worm[player].y + enclose[i].bottomy;
// assert(leftx >= 0 && rightx <= FIELDX && topy >= 0 && bottomy <= FIELDY && leftx < rightx && topy < bottomy);
for (x = leftx; x <= rightx; x++)
{ for (y = topy; y <= bottomy; y++)
{ d = field[x][y];
if ((d >= FIRSTEMPTY && d <= LASTEMPTY) || (d >= FIRSTTAIL && d <= LASTTAIL))
{ if (worm[player].bias)
{ change(x, y, GOLD);
wormscore(player, ENCLOSUREPOINT + TURNTOGOLD);
} elif (player != d - FIRSTTAIL)
{ change(x, y, FIRSTTAIL + player);
wormscore(player, ENCLOSUREPOINT);
} } } } } } }
// move protectors
for (i = 0; i <= PROTECTORS; i++)
{ if (protector[player][i].alive)
{ if (protector[player][i].visible)
{ change(protector[player][i].x, protector[player][i].y, protector[player][i].last);
} else protector[player][i].visible = TRUE;
protector[player][i].last = EMPTY;
if (i == NOSE)
{ protector[player][i].relx = worm[player].deltax * NOSEDISTANCE;
protector[player][i].rely = worm[player].deltay * NOSEDISTANCE;
if (!worm[player].affixer)
{ if (worm[player].position == -1)
worm[player].posidir = 1;
elif (worm[player].position == 1)
worm[player].posidir = -1;
worm[player].position += worm[player].posidir;
if (worm[player].deltax == 0)
protector[player][i].relx = worm[player].position;
elif (worm[player].deltay == 0)
protector[player][i].rely = worm[player].position;
elif (worm[player].position == -1)
protector[player][i].relx = worm[player].deltax * (NOSEDISTANCE - 1);
elif (worm[player].position == 1)
protector[player][i].rely = worm[player].deltay * (NOSEDISTANCE - 1);
} }
elif (!worm[player].affixer)
{ if (protector[player][i].relx == 1 && protector[player][i].rely == -1)
{ protector[player][i].deltax = 0;
protector[player][i].deltay = 1;
} elif (protector[player][i].relx == 1 && protector[player][i].rely == 1)
{ protector[player][i].deltax = -1;
protector[player][i].deltay = 0;
} elif (protector[player][i].relx == -1 && protector[player][i].rely == 1)
{ protector[player][i].deltax = 0;
protector[player][i].deltay = -1;
} elif (protector[player][i].relx == -1 && protector[player][i].rely == -1)
{ protector[player][i].deltax = 1;
protector[player][i].deltay = 0;
}
protector[player][i].relx += protector[player][i].deltax;
protector[player][i].rely += protector[player][i].deltay;
}
protector[player][i].x = worm[player].x + protector[player][i].relx;
protector[player][i].y = worm[player].y + protector[player][i].rely;
if (!valid(protector[player][i].x, protector[player][i].y))
{ protector[player][i].visible = FALSE;
} } }
// head collision detection
wormcol(player, worm[player].x, worm[player].y, enclosed);
// draw head
field[worm[player].x][worm[player].y] = FIRSTHEAD + player;
drawhead(player, worm[player].x, worm[player].y);
updatearrow(worm[player].arrowy);
worm[player].arrowy = worm[player].y;
updatearrow(worm[player].arrowy);
// protector collision detection
for (thisprot = 0; thisprot <= PROTECTORS; thisprot++)
{ if (protector[player][thisprot].alive && protector[player][thisprot].visible)
{ protcol(player, protector[player][thisprot].x, protector[player][thisprot].y, thisprot);
// draw protector
if (protector[player][thisprot].alive && protector[player][thisprot].visible) // in case protector has just been killed, etc.
{ change(protector[player][thisprot].x, protector[player][thisprot].y, FIRSTPROTECTOR + player);
} } }
if (worm[player].cutter)
{ // straight ahead
x = xwrap(worm[player].x + worm[player].deltax);
y = ywrap(worm[player].y + worm[player].deltay);
score += squareblast(HEAD, player, field[x][y], x, y, TRUE);
// left
if (!worm[player].deltax || !worm[player].deltay)
{ // if orthagonal
x = xwrap(worm[player].x + worm[player].deltay);
y = ywrap(worm[player].y - worm[player].deltax);
} else // diagonal
{ if (worm[player].deltax == worm[player].deltay)
{ x = xwrap(worm[player].x + worm[player].deltax);
y = ywrap(worm[player].y - worm[player].deltay);
} else
{ x = xwrap(worm[player].x - worm[player].deltax);
y = ywrap(worm[player].y + worm[player].deltay);
} }
score += squareblast(HEAD, player, field[x][y], x, y, TRUE);
// right
if (!worm[player].deltax || !worm[player].deltay)
{ // if orthagonal
x = xwrap(worm[player].x - worm[player].deltay);
y = ywrap(worm[player].y + worm[player].deltax);
} else // diagonal
{ if (worm[player].deltax == worm[player].deltay)
{ x = xwrap(worm[player].x - worm[player].deltax);
y = ywrap(worm[player].y + worm[player].deltay);
} else
{ x = xwrap(worm[player].x + worm[player].deltax);
y = ywrap(worm[player].y - worm[player].deltay);
} }
score += squareblast(HEAD, player, field[x][y], x, y, TRUE);
// ahead left
if (!worm[player].deltax || !worm[player].deltay)
{ // if orthagonal
if (worm[player].deltax) // if east or west
{ x = xwrap(worm[player].x + worm[player].deltax);
y = ywrap(worm[player].y - worm[player].deltax);
} else // north or south
{ x = xwrap(worm[player].x + worm[player].deltay);
y = ywrap(worm[player].y + worm[player].deltay);
} }
else // diagonal
{ if (worm[player].deltax == worm[player].deltay)
{ x = xwrap(worm[player].x + worm[player].deltax);
y = worm[player].y;
} else
{ x = worm[player].x;
y = ywrap(worm[player].y + worm[player].deltay);
} }
score += squareblast(HEAD, player, field[x][y], x, y, TRUE);
// ahead right
if (!worm[player].deltax || !worm[player].deltay)
{ // if orthagonal
if (worm[player].deltax) // if east or west
{ x = xwrap(worm[player].x + worm[player].deltax);;
y = ywrap(worm[player].y + worm[player].deltax);
} else // north or south
{ x = xwrap(worm[player].x - worm[player].deltay);
y = ywrap(worm[player].y + worm[player].deltay);
} }
else // diagonal
{ if (worm[player].deltax == worm[player].deltay)
{ x = worm[player].x;
y = ywrap(worm[player].y + worm[player].deltay);
} else
{ x = xwrap(worm[player].x + worm[player].deltax);
y = worm[player].y;
} }
score += squareblast(HEAD, player, field[x][y], x, y, TRUE);
wormscore(player, score);
} }
MODULE void protcol(SBYTE player, SBYTE x, SBYTE y, SBYTE thisprot)
{ UBYTE c = field[x][y];
SBYTE i;
ULONG score = 0;
if (c >= FIRSTHEAD && c <= LASTHEAD)
protworm(x, y, player, c - FIRSTHEAD);
elif (c >= FIRSTTAIL && c <= LASTTAIL)
{ if (player == c - FIRSTTAIL || worm[player].mode == TONGUE)
protector[player][thisprot].visible = FALSE;
} elif (c >= FIRSTPROTECTOR && c <= LASTPROTECTOR)
protprot(x, y, player, c - FIRSTPROTECTOR);
elif (c == STONE || c == WOOD || c == METAL || c == TIMEBOMB || c == TELEPORT)
protector[player][thisprot].visible = FALSE;
elif (c >= FIRSTMISSILE && c <= LASTMISSILE)
{ i = whichcreature(x, y, MISSILE, 255);
protmissile(x, y, player, i);
} elif (c == WHIRLWIND)
{ i = whichcreature(x, y, WHIRLWIND, 255);
protwhirlwind(x, y, player, i);
} elif (c == DOG)
{ i = whichcreature(x, y, DOG, 255);
protdog(x, y, player, i);
} elif (c >= FIRSTDRIP && c <= LASTDRIP)
{ i = whichcreature(x, y, DRIP, 255);
protdrip(x, y, player, i);
} elif (c == PENGUIN)
{ i = whichcreature(x, y, PENGUIN, 255);
protpenguin(x, y, player, i);
} elif (c == ORB)
{ i = whichcreature(x, y, ORB, 255);
protorb(x, y, player, i);
} elif (c == FRAGMENT)
{ i = whichcreature(x, y, FRAGMENT, 255);
protfrag(x, y, player, i);
} elif (c == CLOUD)
{ i = whichcreature(x, y, CLOUD, 255);
cloudprot(x, y, i, player);
} elif (c == FIRSTFIRE + player)
{ protector[player][thisprot].visible = FALSE;
} elif (c == OCTOPUS)
{ effect(FXUSE_PROTECTOR);
creature[whichcreature(x, y, OCTOPUS, 255)].alive = FALSE;
protector[player][thisprot].last = BONUS;
score += KILLOCTOPUS;
if (worm[player].bias)
{ worm[player].lives += OCTOPUSBLOOD;
stat(player, LIFE);
} }
elif (c == GOAT)
{ effect(FXUSE_PROTECTOR);
effect(FXDEATH_GOAT);
creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
protector[player][thisprot].last = BONUS;
score += KILLGOAT;
if (worm[player].bias)
{ worm[player].lives += GOATBLOOD;
stat(player, LIFE);
} }
else bothcol(player, x, y);
wormscore(player, score);
}
MODULE void bothcol(SBYTE player, SBYTE x, SBYTE y)
{ UBYTE c = field[x][y];
SBYTE i, j;
ULONG score = 0;
if (c >= FIRSTLETTER && c <= LASTLETTER)
{ wormletter(player, c);
putletter(player);
} elif (c <= LASTOBJECT)
{ score += wormobject(player, x, y);
} else
{ switch(c)
{
case EMPTY:
score += EMPTYPOINT;
break;
case SILVER:
score += SILVERPOINT;
break;
case GOLD:
score += GOLDPOINT;
break;
case DYNAMITE:
effect(FXUSE_BOMB);
banging = TRUE;
bangdynamite(x, y, player);
score += worm[player].dynamitescore;
worm[player].dynamitescore = 0;
break;
case SKULL:
effect(FXGET_SKULL);
score += SKULLPOINT;
j = -1; // for safety
for (i = 0; i <= 3; i++)
{ if (!worm[i].lives && x == worm[i].x && y == worm[i].y && worm[i].control != NONE)
j = i;
}
if (j != -1)
{ worm[player].bias += worm[j].bias;
if (worm[player].bias > 0)
{ if (worm[player].armour > BIASLIMIT)
worm[player].armour = BIASLIMIT;
stat(player, BIAS);
worm[j].bias = 0;
stat(j, BIAS);
}
worm[player].multi *= worm[j].multi;
if (worm[player].multi > 1)
{ if (worm[player].multi > MULTILIMIT)
worm[player].multi = MULTILIMIT;
}
worm[player].power += worm[j].power;
if (worm[player].power > 1)
{ if (worm[player].power > POWERLIMIT)
worm[player].power = POWERLIMIT;
stat(player, POWER);
worm[j].power = 0;
stat(j, POWER);
}
worm[player].ammo += worm[j].ammo;
if (worm[player].ammo > 0)
{ if (worm[player].ammo > AMMOLIMIT)
worm[player].ammo = AMMOLIMIT;
stat(player, AMMO);
worm[j].ammo = 0;
stat(j, AMMO);
}
worm[player].armour += worm[j].armour;
if (worm[player].armour > 0)
{ if (worm[player].armour > MODELIMIT)
worm[player].armour = MODELIMIT;
stat(player, ARMOUR);
worm[j].armour = 0;
stat(j, ARMOUR);
}
worm[player].tongue += worm[j].tongue;
if (worm[player].tongue > 0)
{ if (worm[player].armour > MODELIMIT)
worm[player].armour = MODELIMIT;
stat(player, TONGUE);
worm[j].tongue = 0;
stat(j, TONGUE);
}
if (worm[player].armour > 0 || worm[player].tongue > 0)
{ if (worm[player].armour >= worm[player].tongue)
worm[player].mode = ARMOUR;
else worm[player].mode = TONGUE;
}
if (worm[j].nitro)
{ worm[player].nitro = TRUE;
stat(player, NITRO);
worm[j].nitro = FALSE;
worm[j].speed = NORMAL;
stat(j, NITRO);
}
if (worm[j].affixer)
{ worm[player].affixer = TRUE;
icon(player, AFFIXER);
worm[j].affixer = FALSE;
icon(j, AFFIXER);
}
if (worm[j].remnants)
{ worm[player].remnants = TRUE;
icon(player, REMNANTS);
worm[j].remnants = FALSE;
icon(j, REMNANTS);
}
if (worm[j].sideshot)
{ worm[player].sideshot = TRUE;
icon(player, SIDESHOT);
worm[j].sideshot = FALSE;
icon(j, SIDESHOT);
}
if (worm[j].pusher)
{ worm[player].pusher = TRUE;
icon(player, PUSHER);
worm[j].pusher = FALSE;
icon(j, PUSHER);
}
if (worm[j].cutter)
{ worm[player].cutter += worm[j].cutter;
icon(player, CUTTER);
worm[j].cutter = 0;
icon(j, CUTTER);
}
for (i = 0; i <= LETTERS; i++)
if (letters[j][i])
{ drawletter(j, FIRSTLETTER + i, BLACK);
if (!letters[player][i])
{ letters[player][i] = TRUE;
drawletter(player, FIRSTLETTER + i, NORMAL);
} }
} else ; // assert(0);
break;
default:
break;
} }
wormscore(player, score);
}
AGLOBAL void wormscore(SBYTE player, LONG score)
{ worm[player].score += score * worm[player].multi * players;
stat(player, BONUS);
}
SBYTE xwrap(SBYTE x)
{ if (x < 0)
x += FIELDX + 1;
elif (x > FIELDX)
x -= FIELDX + 1;
return(x);
}
SBYTE ywrap(SBYTE y)
{ if (y < 0)
y += FIELDY + 1;
elif (y > FIELDY)
y -= FIELDY + 1;
return(y);
}
MODULE void ramming(SBYTE player)
{ SBYTE i;
worm[player].rammed = TRUE;
worm[player].x = xwrap(worm[player].x - worm[player].deltax);
worm[player].y = ywrap(worm[player].y - worm[player].deltay);
for (i = 0; i <= PROTECTORS; i++)
{ /* no point checking whether the protectors are alive or dead */
protector[player][i].x -= worm[player].deltax;
protector[player][i].y -= worm[player].deltay;
} }
MODULE SWORD atleast(SWORD value, SWORD minimum)
{ if (value < minimum)
return(minimum);
else return(value);
}
MODULE void orbscore(SBYTE which, ULONG score)
{ creature[which].score += score * creature[which].multi;
}
MODULE void __inline change(SBYTE x, SBYTE y, UBYTE image)
{ // assert(valid(x, y));
field[x][y] = image;
draw(x, y, image);
}
MODULE void createmissile(UBYTE player, SBYTE x, SBYTE y)
{ UBYTE i;
for (i = 0; i <= CREATURES; i++)
if (!creature[i].alive)
{ effect(FXBORN_MISSILE);
creature[i].alive = TRUE;
creature[i].x = x;
creature[i].y = y;
creature[i].species = MISSILE;
creature[i].type = player;
creature[i].last = EMPTY;
creature[i].visible = FALSE;
if (level)
creature[i].speed = (SBYTE) atleast(MISSILESPEED - (level / 2), 1);
else creature[i].speed = BONUSMISSILESPEED;
break;
} }
/* WormWars FSET format for fieldset contents and high score table (Amiga
and IBM-PC), as follows:
header
TEXT[] "FSET x.x" (NULL-terminated)
SBYTE levels;
high score table
for (slot = 0; slot <= HISCORES; slot++)
{ SBYTE hiscore[slot].player,
hiscore[slot].level;
SLONG hiscore[slot].score;
TEXT[] hiscore[slot].name (NULL-terminated)
TEXT[] hiscore[slot].time (NULL-terminated)
TEXT[] hiscore[slot].date (NULL-terminated)
}
level data
for (level = 0; level <= levels; level++)
{ SBYTE startx[level],
starty[level];
ABOOL teleport[level][0].alive;
SBYTE teleport[level][0].x,
teleport[level][0].y;
ABOOL teleport[level][1].alive;
SBYTE teleport[level][1].x,
teleport[level][1].y;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
SBYTE board[level][x][y];
}
version string
TEXT[] "$VER: Worm Wars x.x (dd.mm.yy) $" (NULL-terminated) */
MODULE SBYTE onlyworm(ABOOL alive)
{ SBYTE i,
theworm = -1, // to prevent spurious warnings
worms = 0;
for (i = 0; i <= 3; i++)
if (worm[i].control != NONE && ((!alive) || worm[i].lives))
{ theworm = i;
worms++;
}
if (worms == 1)
return (SBYTE) theworm;
else return -1;
}
/* cloud, prot, worm, dog, drip, frag, missile, orb, penguin, whirlwind
Whichever is earlier in that list comes earlier in the function name. For
example, frag-orb and orb-frag collisions both use fragorb(): there is no
such routine as orbfrag(). Also, there are no such routines as dripdrip()
and penguinpenguin(). */
MODULE void dogdog(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, LIFE);
}
MODULE void dogdrip(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void dogfrag(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, BONUS);
}
MODULE void dogmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, BONUS);
}
MODULE void dogorb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void dogpenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void dogwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void dripfrag(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, BONUS);
}
MODULE void dripmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ /* drip lives, missile dies */
creature[which2].alive = FALSE;
}
MODULE void driporb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
if (creature[which2].mode != ARMOUR)
{ creature[which2].explode = TRUE;
draw(x, y, EMPTY);
} }
MODULE void drippenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void dripwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void fragfrag(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ effect(FXDEATH_FRAGMENT);
creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
draw(x, y, EMPTY);
}
MODULE void fragmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, EMPTY);
}
MODULE void fragorb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
if (creature[which2].mode != ARMOUR)
{ creature[which2].explode = TRUE;
draw(x, y, EMPTY);
} else effect(FXUSE_ARMOUR);
}
MODULE void fragpenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void fragwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void missilemissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, BONUS);
}
MODULE void missilepenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void missilewhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void orborb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ effect(FXDEATH_ORB);
creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, BONUS);
}
MODULE void orbmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (creature[which1].mode != ARMOUR)
{ effect(FXDEATH_ORB);
creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, BONUS);
wormscore(creature[which2].type, creature[which1].score);
if (worm[creature[which2].type].bias)
{ worm[creature[which2].type].lives += ORBBLOOD;
stat(creature[which2].type, LIFE);
} }
else effect(FXUSE_ARMOUR);
}
MODULE void orbpenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
if (creature[which1].mode == ARMOUR)
orbscore(which1, KILLPENGUIN);
else
{ creature[which1].alive = FALSE;
change(x, y, BONUS);
} }
MODULE void orbwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void penguinwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void whirlwindwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, LIFE);
}
MODULE void wormdog(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (creature[which2].dormant == DORMANT)
{ effect(FXBORN_DOG);
creature[which2].dormant = AWAKENING;
creature[which2].type = which1;
worm[which1].last = DOG;
} else
{ creature[which2].alive = FALSE;
if (worm[which1].mode != ARMOUR)
{ worm[which1].alive = FALSE;
worm[which1].cause = DOG;
worm[which1].victor = -1;
} } }
MODULE void wormdrip(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
if (which1 == creature[which2].type)
{ effect(FXGET_DRIP);
wormscore(which1, DRIPBONUS);
if (worm[which1].bias)
{ worm[which1].lives += DRIPBLOOD;
stat(which1, LIFE);
} }
else
{ worm[which1].alive = FALSE;
worm[which1].cause = FIRSTDRIP + creature[which2].type;
worm[which1].victor = -1;
} }
MODULE void wormfrag(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (worm[which1].mode != ARMOUR)
{ worm[which1].cause = FRAGMENT;
worm[which1].victor = -1;
worm[which1].alive = FALSE;
creature[which2].alive = FALSE;
} else
{ effect(FXUSE_ARMOUR);
reflect(which2);
} }
MODULE void wormmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (creature[which2].type == which1)
creature[which2].visible = FALSE;
else
{ creature[which2].alive = FALSE;
if (worm[which1].mode != ARMOUR)
{ worm[which1].cause = FIRSTMISSILE + creature[which2].type;
worm[which1].victor = creature[which2].type;
worm[which1].alive = FALSE;
} else effect(FXUSE_ARMOUR);
} }
MODULE void wormorb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (worm[which1].mode == ARMOUR)
{ effect(FXUSE_ARMOUR);
effect(FXDEATH_ORB);
wormscore(which1, creature[which2].score);
creature[which2].alive = FALSE;
if (worm[which1].bias)
{ worm[which1].lives += ORBBLOOD;
stat(which1, LIFE);
} }
else
{ if (creature[which2].mode == ARMOUR)
{ effect(FXUSE_ARMOUR);
orbscore(which2, KILLWORM);
} else creature[which2].alive = FALSE;
worm[which1].cause = ORB;
worm[which1].victor = -1;
worm[which1].alive = FALSE;
} }
MODULE void wormpenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ effect(FXDEATH_PENGUIN);
creature[which2].alive = FALSE;
if (worm[which1].armour)
{ wormscore(which1, KILLPENGUIN);
if (worm[which1].bias)
{ worm[which1].lives += PENGUINBLOOD;
stat(which1, LIFE);
} }
else
{ worm[which1].alive = FALSE;
worm[which1].cause = PENGUIN;
worm[which1].victor = -1;
} }
MODULE void wormwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ worm[which1].cause = WHIRLWIND;
worm[which1].victor = -1;
worm[which1].alive = FALSE;
}
MODULE void wormworm(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (worm[which1].mode != TONGUE && worm[which2].mode != TONGUE)
{ /* both worms die */
worm[which1].cause = FIRSTHEAD + which2;
worm[which1].alive = FALSE;
worm[which1].victor = -1;
worm[which2].cause = FIRSTHEAD + which1;
worm[which2].alive = FALSE;
worm[which2].victor = -1;
} elif (worm[which1].mode == TONGUE && worm[which2].mode != TONGUE)
{ /* 1st worm lives, 2nd worm dies */
worm[which2].cause = FIRSTHEAD + which1;
worm[which2].alive = FALSE;
worm[which2].victor = which1;
} elif (worm[which1].mode != TONGUE && worm[which2].mode == TONGUE)
{ /* 1st worm dies, 2nd worm lives */
worm[which1].cause = FIRSTHEAD + which2;
worm[which1].alive = FALSE;
worm[which1].victor = which2;
} }
MODULE void protdog(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void protdrip(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
if (which1 == creature[which2].type)
{ effect(FXGET_DRIP);
wormscore(which1, DRIPBONUS);
if (worm[which1].bias)
{ worm[which1].lives += DRIPBLOOD;
stat(which1, LIFE);
} }
else effect(FXUSE_PROTECTOR);
}
MODULE void protfrag(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ effect(FXUSE_PROTECTOR);
reflect(which2);
}
MODULE void protmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (which1 != creature[which2].type)
{ effect(FXUSE_PROTECTOR);
creature[which2].alive = FALSE;
} else creature[which2].visible = FALSE;
}
MODULE void protorb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ effect(FXUSE_PROTECTOR);
effect(FXDEATH_ORB);
wormscore(which1, creature[which2].score);
creature[which2].alive = FALSE;
if (worm[which1].bias)
{ worm[which1].lives += ORBBLOOD;
stat(which1, LIFE);
} }
MODULE void protpenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ effect(FXUSE_PROTECTOR);
effect(FXDEATH_PENGUIN);
wormscore(which1, KILLPENGUIN);
creature[which2].alive = FALSE;
if (worm[which1].bias)
{ worm[which1].lives += PENGUINBLOOD;
stat(which1, LIFE);
} }
MODULE void protwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ UBYTE i;
for (i = 0; i <= PROTECTORS; i++)
if (protector[which1][i].alive && protector[which1][i].x == x && protector[which1][i].y == y)
protector[which1][i].alive = FALSE;
}
MODULE void protworm(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ SBYTE i, j = -1; // to prevent spurious warnings
for (i = 0; i <= PROTECTORS; i++)
if (protector[which1][i].alive && protector[which1][i].x == x && protector[which1][i].y == y)
{ j = i;
break;
}
if (which1 != which2)
{ if (worm[which2].mode != ARMOUR)
{ effect(FXUSE_PROTECTOR);
worm[which2].cause = FIRSTPROTECTOR + which1;
worm[which2].victor = which1;
worm[which2].alive = FALSE;
} else
{ effect(FXUSE_ARMOUR);
protector[which1][j].visible = FALSE;
} }
else
{ /* protector is over worm's own head; caused by ramming */
protector[which1][j].visible = FALSE;
} }
MODULE void protprot(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ SBYTE i, p1 = -1, p2 = -1; // to prevent spurious warnings
/* Find both protectors */
for (i = 0; i <= PROTECTORS; i++)
{ if (protector[which1][i].alive && protector[which1][i].x == x && protector[which1][i].y == y)
p1 = i;
if (protector[which2][i].alive && protector[which2][i].x == x && protector[which2][i].y == y)
p2 = i;
}
protector[which1][p1].alive = FALSE;
protector[which2][p2].alive = FALSE;
change(x, y, EMPTY);
}
MODULE void cloudcloud(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, LIGHTNING);
}
MODULE void clouddog(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void clouddrip(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void cloudfrag(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void cloudmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void cloudorb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void cloudpenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void cloudprot(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void cloudwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void cloudworm(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
if (worm[which2].mode != ARMOUR)
{ worm[which2].alive = FALSE;
worm[which2].cause = CLOUD;
worm[which2].victor = -1;
} }
MODULE ULONG wormobject(UBYTE player, SBYTE x, SBYTE y)
{ AUTO UBYTE c = field[x][y], d;
AUTO ULONG score = object[c].score;
AUTO UBYTE i, j;
AUTO SBYTE xx, xxx, yy, yyy;
AUTO ABOOL done;
PERSIST UBYTE otherfield[FIELDX + 1][FIELDY + 1];
for (i = 0; i <= MAGNETS; i++)
if (magnet[i].alive && x == magnet[i].x && y == magnet[i].y)
magnet[i].alive = FALSE;
if (!valid(x, y)) // defensive programming
{ return 0;
/* AUTO TEXT temp1[SAYLIMIT + 1], temp2[8];
strcpy(temp1, "BAD OBJECT AT x: ");
stci_d(temp2, x);
strcat(temp1, temp2);
strcat(temp1, ", y: ");
stci_d(temp2, y);
strcat(temp1, temp2);
strcat(temp1, "!");
say(temp1, worm[player].colour);
draw(FIELDX + 1, 0, c); // indicates which object
Delay(250);
clearkybd();
anykey(FALSE); */
}
switch(c)
{
case BONUS:
effect(FXGET_OBJECT);
i = rand() % (LETTERS + 1);
letters[player][i] = TRUE;
drawletter(player, FIRSTLETTER + i, NORMAL);
break;
case AMMO:
effect(FXGET_AMMO);
worm[player].ammo += (rand() % 5) + 2; /* 2-6 bullets */
stat(player, AMMO);
break;
case ARMOUR:
effect(FXGET_OBJECT);
worm[player].armour += MODEADD + (rand() % MODERAND);
worm[player].mode = ARMOUR;
stat(player, ARMOUR);
break;
case TONGUE:
effect(FXGET_OBJECT);
worm[player].tongue += MODEADD + (rand() % MODERAND);
worm[player].mode = TONGUE;
stat(player, TONGUE);
worm[player].last = FIRSTTAIL + player;
break;
case NITRO:
effect(FXGET_NITRO);
worm[player].nitro = TRUE;
stat(player, NITRO);
break;
case BOMB:
if (worm[player].mode == NOMODE)
draw(worm[player].x, worm[player].y, eachworm[player][0][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
else draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
bombblast(HEAD, player, worm[player].x, worm[player].y);
break;
case POWER:
effect(FXGET_POWERUP);
if (worm[player].power < POWERLIMIT)
{ worm[player].power += 2;
stat(player, POWER);
}
break;
case SLAYER:
for (i = 0; i <= CREATURES; i++)
if (creature[i].alive)
{ if (creature[i].species == ORB)
{ effect(FXDEATH_ORB);
score += creature[i].score;
creature[i].explode = TRUE;
if (worm[player].bias)
worm[player].lives += ORBBLOOD;
} elif (creature[i].species == GOAT)
{ effect(FXDEATH_GOAT);
creature[i].alive = FALSE;
score += KILLGOAT;
if (worm[player].bias)
worm[player].lives += GOATBLOOD;
change(creature[i].x, creature[i].y, BONUS);
} elif (creature[i].species == OCTOPUS)
{ creature[i].alive = FALSE;
score += KILLOCTOPUS;
if (worm[player].bias)
worm[player].lives += OCTOPUSBLOOD;
change(creature[i].x, creature[i].y, BONUS);
} elif (creature[i].species == PENGUIN)
{ effect(FXDEATH_PENGUIN);
creature[i].alive = FALSE;
score += KILLPENGUIN;
if (worm[player].bias)
worm[player].lives += PENGUINBLOOD;
change(creature[i].x, creature[i].y, EMPTY);
} elif (creature[i].species == DRIP || creature[i].species == CLOUD || (creature[i].species == MISSILE && creature[i].type != player))
{ creature[i].alive = FALSE;
change(creature[i].x, creature[i].y, EMPTY);
} }
for (i = 0; i <= 3; i++)
if (player != i && worm[i].mode != ARMOUR)
{ worm[i].alive = FALSE;
worm[i].cause = SLAYER;
worm[i].victor = player;
}
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == SLIME)
change(x, y, EMPTY);
if (worm[player].bias)
stat(player, LIFE); // we do it here once for efficiency
break;
case PROTECTOR:
done = FALSE;
for (i = 0; i <= PROTECTORS; i++)
if (!protector[player][i].alive && !done)
{ do
{ protector[player][i].relx = ((rand() % 2) * 2) - 1;
protector[player][i].rely = ((rand() % 2) * 2) - 1;
for (j = 0; j <= PROTECTORS; j++)
if (i == NOSE || !protector[player][j].alive || protector[player][j].x != xwrap(worm[player].x + protector[player][i].relx) || protector[player][j].y != ywrap(worm[player].y + protector[player][i].rely))
{ effect(FXBORN_PROTECTOR);
done = TRUE;
protector[player][i].alive = TRUE;
protector[player][i].visible = FALSE;
protector[player][i].last = EMPTY;
if (i == NOSE)
worm[player].position = -1;
}
} while (!done);
}
break;
case MISSILE:
createmissile(player, worm[player].x, worm[player].y);
break;
case LIFE:
effect(FXGET_OBJECT);
worm[player].lives += (rand() % 5) + 2; /* 2-6 lives */
stat(player, LIFE);
break;
case MULTIPLIER:
effect(FXGET_OBJECT);
if (worm[player].multi < MULTILIMIT)
worm[player].multi *= 2;
break;
case BIAS:
effect(FXGET_OBJECT);
worm[player].bias += MODEADD + (rand() % MODERAND);
stat(player, BIAS);
break;
case ICE:
effect(FXGET_OBJECT);
worm[player].ice += ICEADD + (rand() % ICERAND);
ice = player;
icon(player, ICE);
break;
case GROWER:
effect(FXGET_GROWER);
/* grow silver */
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == SILVER)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy))
if (field[xx][yy] == EMPTY)
field[xx][yy] = TEMPSILVER;
/* grow gold */
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == GOLD)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy))
if (field[xx][yy] == EMPTY || field[xx][yy] == TEMPSILVER)
field[xx][yy] = TEMPGOLD;
/* update field */
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
switch (field[x][y])
{
case TEMPGOLD:
change(x, y, GOLD);
break;
case TEMPSILVER:
change(x, y, SILVER);
break;
default:
break;
}
if (worm[player].bias)
{ // grow dynamite
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy))
if (field[xx][yy] == EMPTY || field[xx][yy] == TEMPSILVER)
field[xx][yy] = TEMPTAIL;
// update field
for (x = 0; x <= FIELDX; x++)
{ for (y = 0; y <= FIELDY; y++)
{ if (field[x][y] == TEMPTAIL)
{ change(x, y, DYNAMITE);
} } } }
break;
case TREASURE:
treasurer = player;
if (level)
{ secondsperlevel = 0;
leveltype = rand() % 3;
if (leveltype == 0)
{ say("Bonus Level: Treasury!", worm[treasurer].colour);
leveltype = TREASURE;
} elif (leveltype == 1)
{ say("Bonus Level: Drips!", worm[treasurer].colour);
leveltype = DRIP;
} else
{ // assert(leveltype == 2);
say("Bonus Level: Penguins!", worm[treasurer].colour);
leveltype = PENGUIN;
} }
secondsperlevel += TREASUREADD + (rand() % TREASURERAND);
if (level && leveltype != TREASURE)
secondsperlevel *= 2;
if (secondsperlevel > TIMELIMIT)
secondsperlevel = TIMELIMIT;
if (level)
{ stat(player, BONUS);
reallevel = level;
level = 0;
newlevel(player);
}
break;
case AFFIXER:
effect(FXGET_OBJECT);
worm[player].affixer = TRUE;
icon(player, AFFIXER);
break;
case SWITCHER:
effect(FXGET_OBJECT);
for (x = 0; x <= FIELDX; x++)
{ for (y = 0; y <= FIELDY; y++)
{ if (players >= 2 && field[x][y] >= FIRSTTAIL && field[x][y] <= LASTTAIL && field[x][y] != FIRSTTAIL + player)
{ change(x, y, FIRSTTAIL + player);
} elif (worm[player].bias)
{ if (field[x][y] >= FIRSTFIRE && field[x][y] <= LASTFIRE && field[x][y] != FIRSTFIRE + player)
change(x, y, FIRSTFIRE + player);
elif (field[x][y] == SLIME)
change(x, y, DYNAMITE);
} } }
break;
case HEALER:
effect(FXGET_OBJECT);
if (worm[player].lives < 100)
worm[player].lives = 100;
else worm[player].lives = LIVESLIMIT;
stat(player, LIFE);
break;
case UMBRELLA:
level += (rand() % 2) + 1;
if (level >= levels)
level = levels; // fixed?
for (i = 0; i <= LETTERS; i++)
letters[player][i] = TRUE;
break;
case CLOCK:
effect(FXGET_OBJECT);
if (secondsleft)
{ secondsperlevel += (rand() % CLOCKRAND) + CLOCKADD;
if (secondsperlevel > TIMELIMIT)
secondsperlevel = TIMELIMIT;
}
break;
case SLOWER:
effect(FXGET_OBJECT);
for (i = 0; i <= CREATURES; i++)
if (creature[i].alive && creature[i].species != MISSILE)
creature[i].speed = (SBYTE) atleast(creature[i].speed * 2, VERYFAST);
break;
case PULSE:
explosion(xwrap(worm[player].x - worm[player].deltax),
ywrap(worm[player].y - worm[player].deltay),
worm[player].deltax,
worm[player].deltay,
PULSE);
break;
case REMNANTS:
effect(FXGET_OBJECT);
worm[player].remnants = TRUE;
icon(player, REMNANTS);
break;
case SIDESHOT:
effect(FXGET_POWERUP);
worm[player].sideshot = TRUE;
icon(player, SIDESHOT);
break;
case MAGNET:
effect(FXGET_OBJECT);
i = 0;
field[x][y] = EMPTY; // so that the magnet itself is destroyed
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] <= LASTOBJECT)
{ while (magnet[i].alive && i < MAGNETS)
i++;
if (i > MAGNETS)
{ break;
} else
{ magnet[i].x = xx;
magnet[i].y = yy;
magnet[i].object = field[xx][yy];
magnet[i].player = player;
magnet[i].alive = TRUE;
i++;
} }
break;
case CUTTER:
effect(FXGET_OBJECT);
worm[player].cutter += (rand() % CUTTERRAND) + CUTTERADD;
icon(player, CUTTER);
break;
case CYCLONE:
/* create cyclones */
for (i = 0; i <= CREATURES; i++)
if (!(creature[i].alive))
{ effect(FXGET_CYCLONE);
done = FALSE;
while (!done)
{ creature[i].x = rand() % (FIELDX + 1);
creature[i].y = (rand() % (FIELDY - 2)) + 3;
d = field[creature[i].x][creature[i].y];
if ((d >= FIRSTEMPTY && d <= LASTEMPTY) || (d >= FIRSTTAIL && d <= LASTTAIL))
done = TRUE;
/* Theoretically, this loop could hang... */
}
creature[i].last = EMPTY;
creature[i].speed = VERYFAST;
creature[i].visible = TRUE;
creature[i].deltax = 0;
creature[i].deltay = 0;
creature[i].species = WHIRLWIND;
creature[i].alive = TRUE;
change(creature[i].x, creature[i].y, WHIRLWIND);
break;
}
break;
case LIGHTNING:
effect(FXGET_OBJECT);
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
otherfield[xx][yy] = EMPTY;
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] == FIRSTTAIL + player)
for (xxx = xx - 1; xxx <= xx + 1; xxx++)
for (yyy = yy - 1; yyy <= yy + 1; yyy++)
if (valid(xxx, yyy))
{ d = field[xxx][yyy];
if (d == ORB
|| d == GOAT
|| d == MISSILE
|| d == PENGUIN
|| d == FRAGMENT
|| (d >= FIRSTTAIL && d <= LASTTAIL && d != FIRSTTAIL + player)
|| (d >= FIRSTHEAD && d <= LASTHEAD)
|| (d >= FIRSTDRIP && d <= LASTDRIP)
|| d <= LASTOBJECT)
{ otherfield[xxx][yyy] = TEMPLIGHTNING;
draw(xxx, yyy, LIGHTNING);
} }
for (xx = 0; xx <= FIELDX; xx++)
{ for (yy = 0; yy <= FIELDY; yy++)
{ if (otherfield[xx][yy] == TEMPLIGHTNING)
{ d = field[xx][yy];
switch(d)
{
case ORB:
i = whichcreature(xx, yy, ORB, 255);
if (creature[i].mode == ARMOUR)
draw(xx, yy, ORBARMOUR);
else
{ creature[i].alive = FALSE;
score += creature[i].score;
change(xx, yy, BONUS);
}
break;
case GOAT:
creature[whichcreature(xx, yy, GOAT, 255)].alive = FALSE;
score += KILLGOAT;
change(xx, yy, BONUS);
break;
case MISSILE:
i = whichcreature(xx, yy, MISSILE, 255);
if (player != creature[i].type)
{ creature[i].alive = FALSE;
change(xx, yy, EMPTY);
} else draw(xx, yy, FIRSTMISSILE + player);
break;
case PENGUIN:
score += KILLPENGUIN;
// note no break here
case FRAGMENT:
creature[whichcreature(xx, yy, d, 255)].alive = FALSE;
change(xx, yy, EMPTY);
break;
default:
if (d >= FIRSTDRIP && d <= LASTDRIP)
{ creature[whichcreature(xx, yy, DRIP, 255)].alive = FALSE;
score += DRIPBONUS;
if (player == d - FIRSTDRIP && worm[player].bias)
{ worm[player].lives += DRIPBLOOD;
stat(player, LIFE);
} }
elif (d >= FIRSTHEAD && d <= LASTHEAD)
{ if (player != d - FIRSTHEAD && worm[d - FIRSTHEAD].mode != ARMOUR)
{ worm[d - FIRSTHEAD].alive = FALSE;
worm[d - FIRSTHEAD].cause = LIGHTNING;
worm[d - FIRSTHEAD].victor = player;
change(xx, yy, EMPTY);
} }
else /* eg. tail */
change(xx, yy, EMPTY);
break;
} } } }
break;
case PUSHER:
effect(FXGET_OBJECT);
worm[player].pusher = TRUE;
icon(player, PUSHER);
break;
case FREEDOM:
effect(FXGET_OBJECT);
worm[player].freedom += FREEDOMADD + (rand() % FREEDOMRAND);
if (worm[player].freedom > FREEDOMLIMIT)
worm[player].freedom = FREEDOMLIMIT;
icon(player, FREEDOM);
break;
case CONVERTER:
effect(FXGET_OBJECT);
for (i = 0; i <= CREATURES; i++)
if (creature[i].alive && creature[i].species == FRAGMENT)
{ xx = creature[i].x;
yy = creature[i].y;
creature[i].alive = FALSE;
change(xx, yy, EMPTY);
createmissile(player, xx, yy);
}
break;
default:
// assert(0);
break;
}
return(score);
}
void icon(SBYTE player, UBYTE image)
{ /* Updates one of the boolean icons. The routine checks
the status directly. */
AUTO SBYTE x, y;
if (!worm[player].statx)
x = -7;
else x = FIELDX + 2;
if (!worm[player].staty)
y = (FIELDY / 2) - 3;
else y = (FIELDY / 2) + 3;
switch(image)
{
case AFFIXER:
if (worm[player].affixer)
draw(x, y, AFFIXER);
else draw(x, y, BLACKENED);
break;
case PUSHER:
if (worm[player].pusher)
draw(x + 1, y, PUSHER);
else draw(x + 1, y, BLACKENED);
break;
case REMNANTS:
if (worm[player].remnants)
draw(x + 2, y, REMNANTS);
else draw(x + 2, y, BLACKENED);
break;
case SIDESHOT:
if (worm[player].sideshot)
draw(x + 3, y, SIDESHOT);
else draw(x + 3, y, BLACKENED);
break;
case ICE:
if (worm[player].ice)
if (worm[player].ice < 3)
{ if ((r % 4) <= 1)
draw(x + 4, y, ICE);
else draw(x + 4, y, BLACKENED);
}
else draw(x + 4, y, ICE);
else draw(x + 4, y, BLACKENED);
break;
case CUTTER:
if (worm[player].cutter)
if (worm[player].cutter < 10)
{ if ((r % 4) <= 1)
draw(x + 5, y, CUTTER);
else draw(x + 5, y, BLACKENED);
}
else draw(x + 5, y, CUTTER);
else draw(x + 5, y, BLACKENED);
break;
case FREEDOM:
if (worm[player].freedom)
if (worm[player].freedom < 10)
{ if ((r % 4) <= 1)
draw(x + 6, y, FREEDOM);
else draw(x + 6, y, BLACKENED);
}
else draw(x + 6, y, FREEDOM);
else draw(x + 6, y, BLACKENED);
break;
default:
break;
} }
MODULE void wormcol(SBYTE player, SBYTE x, SBYTE y, ABOOL enclosed)
{ ABOOL flag;
UBYTE c = field[x][y], d;
SBYTE i, xx, yy;
ULONG score = 0;
if (c >= FIRSTHEAD && c <= LASTHEAD)
wormworm(x, y, player, c - FIRSTHEAD);
elif (c == TIMEBOMB)
{ /* push timebomb */
i = whichcreature(x, y, TIMEBOMB, 255);
if (valid(x + worm[player].deltax, y + worm[player].deltay))
{ d = field[x + worm[player].deltax][y + worm[player].deltay];
if (d <= LASTEMPTY)
{ creature[i].x += worm[player].deltax;
creature[i].y += worm[player].deltay;
field[creature[i].x][creature[i].y] = TIMEBOMB;
draw(creature[i].x, creature[i].y, ZERO + creature[i].time);
} else
{ if (worm[player].mode == NOMODE)
draw(worm[player].x, worm[player].y, eachworm[player][0][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
else draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
bombblast(HEAD, player, worm[player].x, worm[player].y);
creature[i].alive = FALSE;
} }
else score += BOMBOVEREDGE;
} elif (c >= FIRSTPROTECTOR && c <= LASTPROTECTOR)
protworm(x, y, c - FIRSTPROTECTOR, player);
elif (c >= FIRSTMISSILE && c <= LASTMISSILE)
{ i = whichcreature(x, y, MISSILE, 255);
wormmissile(x, y, player, i);
} elif (c >= FIRSTDRIP && c <= LASTDRIP)
{ i = whichcreature(x, y, DRIP, 255);
wormdrip(x, y, player, i);
} elif (c == STONE || c == GOAT || c == OCTOPUS || c == METAL || (c >= FIRSTTAIL && c <= LASTTAIL))
{ flag = TRUE;
if (worm[player].pusher)
{ xx = x + worm[player].deltax;
yy = y + worm[player].deltay;
if (valid(xx, yy))
{ d = field[xx][yy];
if (d <= LASTEMPTY)
{ flag = FALSE;
if (c == GOAT || c == OCTOPUS)
{ i = whichcreature(x, y, c, 255);
creature[i].x = xx;
creature[i].y = yy;
creature[i].visible = FALSE;
}
field[xx][yy] = c;
draw(xx, yy, c);
} }
else
{ flag = FALSE;
score += BOMBOVEREDGE;
} }
if (flag)
{ if (c >= FIRSTTAIL && c <= LASTTAIL)
{ if (worm[player].mode == TONGUE)
{ effect(FXUSE_TONGUE);
if (players > 1)
if (player == c - FIRSTTAIL)
{ score += TURNTOSILVER;
worm[player].last = SILVER;
} else
{ score += TURNTOGOLD;
worm[player].last = GOLD;
} }
elif (!enclosed)
{ worm[player].cause = c;
worm[player].alive = FALSE;
worm[player].victor = c - FIRSTTAIL;
} }
else
{ worm[player].cause = c;
worm[player].victor = -1;
worm[player].alive = FALSE;
ramming(player);
} } }
elif (c == WOOD)
{ if (worm[player].mode != ARMOUR)
{ worm[player].cause = WOOD;
worm[player].alive = FALSE;
worm[player].victor = -1;
} }
elif (c == SLIME)
{ if (worm[player].mode != ARMOUR)
{ worm[player].cause = SLIME;
worm[player].alive = FALSE;
worm[player].victor = -1;
} }
elif (c == PENGUIN)
{ i = whichcreature(x, y, PENGUIN, 255);
wormpenguin(x, y, player, i);
} elif (c == WHIRLWIND)
{ i = whichcreature(x, y, WHIRLWIND, 255);
wormwhirlwind(x, y, player, i);
} elif (c == DOG)
{ i = whichcreature(x, y, DOG, 255);
wormdog(x, y, player, i);
} elif (c == ORB)
{ i = whichcreature(x, y, ORB, 255);
wormorb(x, y, player, i);
} elif (c == FRAGMENT)
{ i = whichcreature(x, y, FRAGMENT, 255);
wormfrag(x, y, player, i);
} elif (c == CLOUD)
{ i = whichcreature(x, y, CLOUD, 255);
cloudworm(x, y, i, player);
} elif (c == TELEPORT)
{ i = whichteleport(x, y);
if (blocked(i, worm[player].deltax, worm[player].deltay))
{ worm[player].cause = TELEPORT;
worm[player].victor = -1;
worm[player].alive = FALSE;
ramming(player);
} else
{ effect(FXUSE_TELEPORT);
score += TELPOINT;
worm[player].x = xwrap(teleport[level][partner(i)].x + worm[player].deltax);
worm[player].y = ywrap(teleport[level][partner(i)].y + worm[player].deltay);
} }
elif (c >= FIRSTFIRE && c <= LASTFIRE)
{ if (player != c - FIRSTFIRE && worm[player].mode != ARMOUR)
{ worm[player].cause = REMNANTS;
worm[player].victor = c - FIRSTFIRE;
worm[player].alive = FALSE;
} }
else bothcol(player, x, y);
wormscore(player, score);
}
AGLOBAL void drawhead(SBYTE player, SBYTE x, SBYTE y)
{ if (worm[player].alive)
{ if (worm[player].mode == NOMODE)
{ draw(worm[player].x, worm[player].y, eachworm[player][0][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
worm[player].flashed = FALSE;
} else
{ // assert(worm[player].mode == TONGUE || worm[player].mode == ARMOUR);
if ((worm[player].mode == TONGUE && worm[player].tongue < 10)
|| (worm[player].mode == ARMOUR && worm[player].armour < 10))
{ if (!worm[player].flashed)
draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
else draw(worm[player].x, worm[player].y, WHITENED);
worm[player].flashed = !worm[player].flashed;
} else draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
} }
else draw(worm[player].x, worm[player].y, SKULL);
}
AGLOBAL void drawsquare(SBYTE x, SBYTE y)
{ UBYTE which;
if (field[x][y] == DOG)
{ which = whichcreature(x, y, DOG, 255);
if (!creature[which].dormant)
draw(x, y, DOGDORMANT);
elif (creature[which].dormant <= CHASING)
draw(x, y, DOGAWAKENING);
else draw(x, y, DOG);
} elif (field[x][y] >= FIRSTHEAD && field[x][y] <= LASTHEAD)
drawhead(field[x][y] - FIRSTHEAD, x, y);
else draw(x, y, field[x][y]);
}
MODULE void updatearrow(SBYTE arrowy)
{ SBYTE i, var = -1;
/* var of:
-2 : many there
-1 : nothing there
0-3 : just that worm,
FIRSTLETTER-LASTLETTER: just that letter */
for (i = 0; i <= 3; i++)
if (worm[i].control != NONE && worm[i].y == arrowy && (worm[i].lives || field[worm[i].x][worm[i].y] == SKULL))
if (var == -1)
var = i;
else var = -2;
if (lettery == arrowy)
if (var == -1)
var = lettertype;
else var = -2;
if (var == -2)
draw(ARROWX, arrowy, ALL);
elif (var == -1)
draw(ARROWX, arrowy, BLACKENED);
elif (var >= FIRSTLETTER && var <= LASTLETTER)
draw(ARROWX, arrowy, var);
else
{ // assert(var >= 0 && var <= 3);
if (worm[var].lives)
draw(ARROWX, arrowy, FIRSTARROW + var);
else draw(ARROWX, arrowy, SKULL);
} }
MODULE void __inline bangdynamite(SBYTE x, SBYTE y, SBYTE player)
{ SBYTE xx, yy;
// Infects (turns to bang-dynamite) all surrounding dynamite.
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy))
if (field[xx][yy] == DYNAMITE)
{ field[xx][yy] = TEMPBANGDYNAMITE;
worm[player].dynamitescore += DYNAMITEPOINT;
infector[xx][yy] = player;
}
}
MODULE void octopusfire(UBYTE which)
{ UBYTE i;
SBYTE x, y;
for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ switch(creature[which].dir)
{
case 0:
creature[i].deltax = 0;
creature[i].deltay = -1;
break;
case 1:
creature[i].deltax = 1;
creature[i].deltay = -1;
break;
case 2:
creature[i].deltax = 1;
creature[i].deltay = 0;
break;
case 3:
creature[i].deltax = 1;
creature[i].deltay = 1;
break;
case 4:
creature[i].deltax = 0;
creature[i].deltay = 1;
break;
case 5:
creature[i].deltax = -1;
creature[i].deltay = 1;
break;
case 6:
creature[i].deltax = -1;
creature[i].deltay = 0;
break;
case 7:
creature[i].deltax = -1;
creature[i].deltay = -1;
break;
default:
break;
}
x = creature[which].x + creature[i].deltax;
y = creature[which].y + creature[i].deltay;
if (valid(x, y) && field[x][y] >= FIRSTEMPTY && field[x][y] <= LASTEMPTY)
{ effect(FXGET_PULSE);
creature[i].last = EMPTY;
creature[i].visible = TRUE;
creature[i].x = x;
creature[i].y = y;
creature[i].speed = octopusspeed;
creature[i].species = FRAGMENT;
creature[i].alive = TRUE;
change(x, y, FRAGMENT);
goto out;
} } }
out:
if (++creature[which].dir == 8)
{ creature[which].dir = -1;
} }